diff --git a/app/__init__.py b/app/__init__.py
index 217075ac2..bd2c03387 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -100,8 +100,6 @@ from app.notify_client.events_api_client import events_api_client
from app.notify_client.inbound_number_client import inbound_number_client
from app.notify_client.invite_api_client import invite_api_client
from app.notify_client.job_api_client import job_api_client
-from app.notify_client.letter_branding_client import letter_branding_client
-from app.notify_client.letter_jobs_client import letter_jobs_client
from app.notify_client.notification_api_client import notification_api_client
from app.notify_client.org_invite_api_client import org_invite_api_client
from app.notify_client.organisations_api_client import organisations_client
@@ -123,7 +121,6 @@ from app.notify_client.template_statistics_api_client import (
from app.notify_client.upload_api_client import upload_api_client
from app.notify_client.user_api_client import user_api_client
from app.url_converters import (
- LetterFileExtensionConverter,
SimpleDateTypeConverter,
TemplateTypeConverter,
TicketTypeConverter,
@@ -188,8 +185,6 @@ def create_app(application):
inbound_number_client,
invite_api_client,
job_api_client,
- letter_branding_client,
- letter_jobs_client,
notification_api_client,
org_invite_api_client,
organisations_client,
@@ -278,7 +273,6 @@ def init_app(application):
application.url_map.converters['uuid'].to_python = lambda self, value: value
application.url_map.converters['template_type'] = TemplateTypeConverter
application.url_map.converters['ticket_type'] = TicketTypeConverter
- application.url_map.converters['letter_file_extension'] = LetterFileExtensionConverter
application.url_map.converters['simple_date'] = SimpleDateTypeConverter
@@ -501,8 +495,8 @@ def setup_blueprints(application):
no_cookie_blueprint is for subresources (things loaded asynchronously) that we might be concerned are setting
cookies unnecessarily and potentially getting in to strange race conditions and overwriting other cookies, as we've
- seen in the send message flow. Currently, this includes letter template previews, and the iframe from the platform
- admin email branding preview pages.
+ seen in the send message flow. Currently, this includes the iframe from the platform admin email branding
+ preview pages.
This notably doesn't include the *.json ajax endpoints. If we included them in this, the cookies wouldn't be
updated, including the expiration date. If you have a dashboard open and in focus it'll refresh the expiration timer
diff --git a/app/assets/stylesheets/components/letter.scss b/app/assets/stylesheets/components/letter.scss
deleted file mode 100644
index d23408248..000000000
--- a/app/assets/stylesheets/components/letter.scss
+++ /dev/null
@@ -1,100 +0,0 @@
-$iso-paper-ratio: 141.42135624%;
-
-@keyframes ellipsis {
- to {
- width: 1.25em;
- }
-}
-
-.letter {
-
- padding: $iso-paper-ratio 0 0 0;
- margin: 0 0 govuk-spacing(6) 0;
- position: relative;
- background: $panel-colour;
-
- &:before {
- position: absolute;
- top: 10%;
- left: 50%;
- margin-left: -0.5em;
- font-size: 96px;
- color: $white;
- overflow: hidden;
- display: block;
- vertical-align: bottom;
- animation: ellipsis steps(4,end) 1.3s infinite;
- content: "\2026"; // ellipsis
- width: 0px;
- }
-
- &:after {
- content: "";
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- box-shadow: inset 0 0 0 1px $border-colour;
- }
-
- &-postage {
-
- $art-width: 97.83;
- $art-height: 82.27;
- $fold-height: 124px;
- $envelope-colour: #C4B186;
-
- position: absolute;
- top: 0;
- right: 0;
- z-index: 10;
- width: $fold-height * ($art-width / $art-height);
- height: $fold-height;
- margin: 0;
- background-color: mix($envelope-colour, $grey-1);
- background-size: auto $fold-height;
- background-position: right 0;
- background-repeat: no-repeat;
- background-origin: border-box;
- text-indent: -1000em;
- border-bottom: 1px solid $border-colour;
- border-left: 1px solid $border-colour;
- box-shadow: 0 2px 0 0 rgba($border-colour, 0.2);
-
- &-first {
- background-image: file-url('envelope-1st-class.svg');
- }
-
- &-second {
- background-image: file-url('envelope-2nd-class.svg');
- }
-
- &-international {
- background-image: file-url('envelope-international.svg');
- }
-
- .letter-sent &:hover {
- background-color: transparent;
- border-color: transparent;
- box-shadow: none;
- background-image: file-url('envelope-fold.svg');
- }
-
- }
-
- img {
- display: block;
- width: 100%;
- background: $white;
- position: absolute;
- top: 0;
- left: 0;
- }
-
-}
-
-.letter-recipient-summary {
- line-height: 28px;
- margin-bottom: 0;
-}
diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss
index 1dce511ad..6b09332c5 100644
--- a/app/assets/stylesheets/main.scss
+++ b/app/assets/stylesheets/main.scss
@@ -60,7 +60,6 @@ $path: '/static/images/';
@import 'components/research-mode';
@import 'components/tick-cross';
@import 'components/list-entry';
-@import 'components/letter';
@import 'components/live-search';
@import 'components/stick-at-top-when-scrolling';
@import 'components/fullscreen-table';
diff --git a/app/assets/stylesheets/views/template.scss b/app/assets/stylesheets/views/template.scss
index 4978281e3..90f574333 100644
--- a/app/assets/stylesheets/views/template.scss
+++ b/app/assets/stylesheets/views/template.scss
@@ -26,36 +26,6 @@
}
-.edit-template-link-letter-contact {
- @extend %edit-template-link;
- right: -25px;
- top: 232px; // align to bottom of contact block
-}
-
-.edit-template-link-letter-address {
- @extend %edit-template-link;
- top: 14.65%; // align bottom edge to bottom of address
- left: -5px;
-}
-
-.edit-template-link-letter-body {
- @extend %edit-template-link;
- top: 400px; // aligns to top of subject
- left: -5px;
-}
-
-.edit-template-link-letter-postage {
- @extend %edit-template-link;
- top: 51px; // aligns bottom edge to bottom of postmark
- right: 145px; // Aligns right edge to midpoint of postmark and fold
-}
-
-.edit-template-link-letter-branding {
- @extend %edit-template-link;
- top: 51px; // aligns with ‘change postage’ link
- left: 66px; // Aligns to left of logo area
-}
-
.template-content-count {
@include core-19($tabular-numbers: true);
color: $secondary-text-colour;
diff --git a/app/formatters.py b/app/formatters.py
index c00f6d85c..90d71db2d 100644
--- a/app/formatters.py
+++ b/app/formatters.py
@@ -202,7 +202,6 @@ def format_notification_type(notification_type):
return {
'email': 'Email',
'sms': 'Text message',
- 'letter': 'Letter'
}[notification_type]
@@ -229,23 +228,6 @@ def format_notification_status(status, template_type):
'pending': 'Sending',
'sent': 'Sent to an international number'
},
- 'letter': {
- 'failed': '',
- 'technical-failure': 'Technical failure',
- 'temporary-failure': '',
- 'permanent-failure': 'Permanent failure',
- 'delivered': '',
- 'received': '',
- 'accepted': '',
- 'sending': '',
- 'created': '',
- 'sent': '',
- 'pending-virus-check': '',
- 'virus-scan-failed': 'Virus detected',
- 'returned-letter': '',
- 'cancelled': '',
- 'validation-failed': 'Validation failed',
- }
}[template_type].get(status, status)
@@ -256,23 +238,7 @@ def format_notification_status_as_time(status, created, updated):
def format_notification_status_as_field_status(status, notification_type):
- return {
- 'letter': {
- 'failed': 'error',
- 'technical-failure': 'error',
- 'temporary-failure': 'error',
- 'permanent-failure': 'error',
- 'delivered': None,
- 'sent': None,
- 'sending': None,
- 'created': None,
- 'accepted': None,
- 'pending-virus-check': None,
- 'virus-scan-failed': 'error',
- 'returned-letter': None,
- 'cancelled': 'error',
- },
- }.get(
+ return {}.get(
notification_type,
{
'failed': 'error',
@@ -314,6 +280,7 @@ def nl2br(value):
# this formatter appears to only be used in the letter module
+# TODO: use more widely, or delete? currency symbol could be set in config
def format_number_in_pounds_as_currency(number):
if number >= 1:
return f"£{number:,.2f}"
@@ -455,12 +422,6 @@ def message_count_noun(count, template_type):
else:
return 'emails'
- elif template_type == 'letter':
- if count == 1:
- return 'letter'
- else:
- return 'letters'
-
def message_count(count, template_type):
return (
@@ -489,12 +450,6 @@ def recipient_count_label(count, template_type):
else:
return 'email addresses'
- elif template_type == 'letter':
- if count == 1:
- return 'address'
- else:
- return 'addresses'
-
def recipient_count(count, template_type):
return (
diff --git a/app/main/__init__.py b/app/main/__init__.py
index e470b3520..97ec7a815 100644
--- a/app/main/__init__.py
+++ b/app/main/__init__.py
@@ -21,7 +21,6 @@ from app.main.views import ( # noqa isort:skip
index,
invites,
jobs,
- letter_branding,
manage_users,
new_password,
notifications,
@@ -31,7 +30,6 @@ from app.main.views import ( # noqa isort:skip
pricing,
providers,
register,
- returned_letters,
security_policy,
send,
service_settings,
diff --git a/app/main/forms.py b/app/main/forms.py
index 65f40f218..6a7984023 100644
--- a/app/main/forms.py
+++ b/app/main/forms.py
@@ -10,7 +10,6 @@ from flask_wtf import FlaskForm as Form
from flask_wtf.file import FileAllowed
from flask_wtf.file import FileField as FileField_wtf
from flask_wtf.file import FileSize
-from notifications_utils.countries.data import Postage
from notifications_utils.formatters import strip_all_whitespace
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.postal_address import PostalAddress
@@ -1294,110 +1293,12 @@ class SMSTemplateForm(BaseTemplateForm):
OnlySMSCharacters(template_type='sms')(None, field)
-class LetterAddressForm(StripWhitespaceForm):
-
- def __init__(self, *args, allow_international_letters=False, **kwargs):
- self.allow_international_letters = allow_international_letters
- super().__init__(*args, **kwargs)
-
- address = PostalAddressField(
- 'Address',
- validators=[DataRequired(message="Cannot be empty")]
- )
-
- def validate_address(self, field):
-
- address = PostalAddress(
- field.data,
- allow_international_letters=self.allow_international_letters,
- )
-
- if not address.has_enough_lines:
- raise ValidationError(
- f'Address must be at least {PostalAddress.MIN_LINES} lines long'
- )
-
- if address.has_too_many_lines:
- raise ValidationError(
- f'Address must be no more than {PostalAddress.MAX_LINES} lines long'
- )
-
- if not address.has_valid_last_line:
- if self.allow_international_letters:
- raise ValidationError(
- 'Last line of the address must be a UK postcode or another country'
- )
- if address.international:
- raise ValidationError(
- 'You do not have permission to send letters to other countries'
- )
- raise ValidationError(
- 'Last line of the address must be a real UK postcode'
- )
-
- if address.has_invalid_characters:
- raise ValidationError(
- 'Address lines must not start with any of the following characters: @ ( ) = [ ] ” \\ / , < > ~'
- )
-
-
class EmailTemplateForm(BaseTemplateForm):
subject = TextAreaField(
u'Subject',
validators=[DataRequired(message="Cannot be empty")])
-class LetterTemplateForm(EmailTemplateForm):
- subject = TextAreaField(
- u'Main heading',
- validators=[DataRequired(message="Cannot be empty")])
-
- template_content = TextAreaField(
- u'Body',
- validators=[
- DataRequired(message="Cannot be empty"),
- NoCommasInPlaceHolders()
- ]
- )
-
-
-class LetterTemplatePostageForm(StripWhitespaceForm):
- postage = GovukRadiosField(
- 'Choose the postage for this letter template',
- choices=[
- ('first', 'First class'),
- ('second', 'Second class'),
- ],
- thing='first class or second class',
- validators=[DataRequired()]
- )
-
-
-class LetterUploadPostageForm(StripWhitespaceForm):
-
- def __init__(self, *args, postage_zone, **kwargs):
-
- super().__init__(*args, **kwargs)
-
- if postage_zone != Postage.UK:
- self.postage.choices = [(postage_zone, '')]
- self.postage.data = postage_zone
-
- @property
- def show_postage(self):
- return len(self.postage.choices) > 1
-
- postage = GovukRadiosField(
- 'Choose the postage for this letter',
- choices=[
- ('first', 'First class post'),
- ('second', 'Second class post'),
- ],
- default='second',
- validators=[DataRequired()]
- )
-
-
class ForgotPasswordForm(StripWhitespaceForm):
email_address = email_address(gov_user=False)
@@ -1552,11 +1453,6 @@ class EstimateUsageForm(StripWhitespaceForm):
things='text messages',
format_error_suffix='you expect to send',
)
- volume_letter = ForgivingIntegerField(
- 'How many letters do you expect to send in the next year?',
- things='letters',
- format_error_suffix='you expect to send',
- )
consent_to_research = GovukRadiosField(
'Can we contact you when we’re doing user research?',
choices=[
@@ -1573,7 +1469,7 @@ class EstimateUsageForm(StripWhitespaceForm):
def validate(self, *args, **kwargs):
- if self.volume_email.data == self.volume_sms.data == self.volume_letter.data == 0:
+ if self.volume_email.data == self.volume_sms.data == 0:
self.at_least_one_volume_filled = False
return False
@@ -1696,23 +1592,6 @@ class AdminBillingDetailsForm(StripWhitespaceForm):
notes = TextAreaField(validators=[])
-class ServiceLetterContactBlockForm(StripWhitespaceForm):
- letter_contact_block = TextAreaField(
- validators=[
- DataRequired(message="Cannot be empty"),
- NoCommasInPlaceHolders()
- ]
- )
- is_default = GovukCheckboxField("Set as your default address")
-
- def validate_letter_contact_block(self, field):
- line_count = field.data.strip().count('\n')
- if line_count >= 10:
- raise ValidationError(
- 'Contains {} lines, maximum is 10'.format(line_count + 1)
- )
-
-
class ServiceOnOffSettingForm(StripWhitespaceForm):
def __init__(self, name, *args, truthy='On', falsey='Off', **kwargs):
@@ -1731,7 +1610,6 @@ class ServiceSwitchChannelForm(ServiceOnOffSettingForm):
name = 'Send {}'.format({
'email': 'emails',
'sms': 'text messages',
- 'letter': 'letters',
}.get(channel))
super().__init__(name, *args, **kwargs)
@@ -1761,11 +1639,6 @@ class AdminSetEmailBrandingForm(StripWhitespaceForm):
)
-class AdminSetLetterBrandingForm(AdminSetEmailBrandingForm):
- # form is the same, but instead of GOV.UK we have None as a valid option
- DEFAULT = (FieldWithNoneOption.NONE_OPTION_VALUE, 'None')
-
-
class AdminPreviewBrandingForm(StripWhitespaceForm):
branding_style = HiddenFieldWithNoneOption('branding_style')
@@ -1800,10 +1673,6 @@ class AdminEditEmailBrandingForm(StripWhitespaceForm):
raise ValidationError('This field is required')
-class AdminEditLetterBrandingForm(StripWhitespaceForm):
- name = GovukTextInputField('Name of brand', validators=[DataRequired()])
-
-
class SVGFileUpload(StripWhitespaceForm):
file = FileField_wtf(
'Upload an SVG logo',
@@ -1816,16 +1685,6 @@ class SVGFileUpload(StripWhitespaceForm):
)
-class PDFUploadForm(StripWhitespaceForm):
- file = FileField_wtf(
- 'Upload a letter in PDF format',
- validators=[
- FileAllowed(['pdf'], 'Save your letter as a PDF and try again.'),
- DataRequired(message="You need to choose a file to upload")
- ]
- )
-
-
class EmailFieldInGuestList(GovukEmailField, StripWhitespaceStringField):
pass
@@ -2062,32 +1921,6 @@ class ChooseEmailBrandingForm(ChooseBrandingForm):
)
-class ChooseLetterBrandingForm(ChooseBrandingForm):
- options = RadioField('Choose your new letter branding')
- something_else = TextAreaField('Describe the branding you want')
-
- def __init__(self, service):
- super().__init__()
-
- self.options.choices = tuple(
- list(branding.get_letter_choices(service)) +
- [self.FALLBACK_OPTION]
- )
-
- if self.something_else_is_only_option:
- self.options.data = self.FALLBACK_OPTION_VALUE
-
- def validate_something_else(self, field):
- if (
- self.something_else_is_only_option
- or self.options.data == self.FALLBACK_OPTION_VALUE
- ) and not field.data:
- raise ValidationError('Cannot be empty')
-
- if self.options.data != self.FALLBACK_OPTION_VALUE:
- field.data = ''
-
-
class SomethingElseBrandingForm(StripWhitespaceForm):
something_else = GovukTextareaField(
'Describe the branding you want',
@@ -2111,7 +1944,6 @@ class AdminServiceAddDataRetentionForm(StripWhitespaceForm):
choices=[
('email', 'Email'),
('sms', 'SMS'),
- ('letter', 'Letter'),
],
thing='notification type',
)
@@ -2128,15 +1960,6 @@ class AdminServiceEditDataRetentionForm(StripWhitespaceForm):
)
-class AdminReturnedLettersForm(StripWhitespaceForm):
- references = TextAreaField(
- u'Letter references',
- validators=[
- DataRequired(message="Cannot be empty"),
- ]
- )
-
-
class TemplateFolderForm(StripWhitespaceForm):
def __init__(self, all_service_users=None, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -2214,7 +2037,6 @@ class TemplateAndFoldersSelectionForm(Form):
self.add_template_by_template_type.choices = list(filter(None, [
# ('email', 'Email') if 'email' in available_template_types else None,
('sms', 'Text message') if 'sms' in available_template_types else None,
- # ('letter', 'Letter') if 'letter' in available_template_types else None,
('copy-existing', 'Copy an existing template') if allow_adding_copy_of_template else None,
]))
diff --git a/app/main/views/api_keys.py b/app/main/views/api_keys.py
index 0c1c4daff..d0f6d3c85 100644
--- a/app/main/views/api_keys.py
+++ b/app/main/views/api_keys.py
@@ -96,8 +96,6 @@ def create_api_key(service_id):
'trial mode')
}
}
- if current_service.has_permission('letter'):
- form.key_type.param_extensions['items'][1]['hint'] = {'text': 'Cannot be used to send letters'}
if form.validate_on_submit():
if current_service.trial_mode and form.key_type.data == KEY_TYPE_NORMAL:
abort(400)
diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py
index 24b853f0f..60404d781 100644
--- a/app/main/views/dashboard.py
+++ b/app/main/views/dashboard.py
@@ -258,7 +258,6 @@ def aggregate_template_usage(template_statistics, sort_key='count'):
"template_id": k,
"template_name": template_stats[0]['template_name'],
"template_type": template_stats[0]['template_type'],
- "is_precompiled_letter": template_stats[0]['is_precompiled_letter'],
"count": sum(s['count'] for s in template_stats)
})
@@ -270,7 +269,7 @@ def aggregate_notifications_stats(template_statistics):
notifications = {
template_type: {
status: 0 for status in ('requested', 'delivered', 'failed')
- } for template_type in ["sms", "email", "letter"]
+ } for template_type in ["sms", "email"]
}
for stat in template_statistics:
notifications[stat["template_type"]]["requested"] += stat["count"]
diff --git a/app/main/views/index.py b/app/main/views/index.py
index 836adf39d..44719f274 100644
--- a/app/main/views/index.py
+++ b/app/main/views/index.py
@@ -8,9 +8,9 @@ from flask import (
url_for,
)
from flask_login import current_user
-from notifications_utils.template import HTMLEmailTemplate, LetterImageTemplate
+from notifications_utils.template import HTMLEmailTemplate
-from app import email_branding_client, letter_branding_client, status_api_client
+from app import email_branding_client, status_api_client
from app.main import main
from app.main.forms import FieldWithNoneOption
from app.main.views.pricing import CURRENT_SMS_RATE
@@ -156,35 +156,6 @@ def email_template():
return resp
-@main.route('/_letter')
-def letter_template():
- branding_style = request.args.get('branding_style')
-
- if branding_style == FieldWithNoneOption.NONE_OPTION_VALUE:
- branding_style = None
-
- if branding_style:
- filename = letter_branding_client.get_letter_branding(branding_style)['filename']
- else:
- filename = 'no-branding'
-
- template = {'subject': '', 'content': '', 'template_type': 'letter'}
- image_url = url_for('no_cookie.letter_branding_preview_image', filename=filename)
-
- template_image = str(LetterImageTemplate(
- template,
- image_url=image_url,
- page_count=1,
- ))
-
- resp = make_response(
- render_template('views/service-settings/letter-preview.html', template=template_image)
- )
-
- resp.headers['X-Frame-Options'] = 'SAMEORIGIN'
- return resp
-
-
@main.route('/documentation')
def documentation():
return render_template(
@@ -238,14 +209,6 @@ def features_sms():
)
-@main.route('/features/letters')
-def features_letters():
- return render_template(
- 'views/features/letters.html',
- navigation_links=features_nav()
- )
-
-
@main.route('/features/security', endpoint='security')
def security():
return render_template(
@@ -350,22 +313,6 @@ def send_files_by_email():
)
-@main.route('/using-notify/guidance/upload-a-letter')
-def upload_a_letter():
- return render_template(
- 'views/guidance/upload-a-letter.html',
- navigation_links=using_notify_nav(),
- )
-
-
-@main.route('/using-notify/guidance/letter-specification')
-def letter_specification():
- return render_template(
- 'views/guidance/letter-specification.html',
- navigation_links=using_notify_nav(),
- )
-
-
# --- Redirects --- #
@main.route('/roadmap', endpoint='old_roadmap')
@@ -384,11 +331,3 @@ def old_page_redirects():
'main.old_integration_testing': 'main.integration_testing',
}
return redirect(url_for(redirects[request.endpoint]), code=301)
-
-
-@main.route('/docs/notify-pdf-letter-spec-latest.pdf')
-def letter_spec():
- return redirect(
- 'https://docs.notifications.service.gov.uk'
- '/documentation/images/notify-pdf-letter-spec-v2.4.pdf'
- )
diff --git a/app/main/views/jobs.py b/app/main/views/jobs.py
index 54e292fe5..e716e7694 100644
--- a/app/main/views/jobs.py
+++ b/app/main/views/jobs.py
@@ -16,7 +16,6 @@ from flask import (
from flask_login import current_user
from notifications_utils.template import (
EmailPreviewTemplate,
- LetterPreviewTemplate,
SMSBodyPreviewTemplate,
)
@@ -135,7 +134,6 @@ def view_notifications(service_id, message_type=None):
things_you_can_search_by={
'email': ['email address'],
'sms': ['phone number'],
- 'letter': ['postal address', 'file name'],
# We say recipient here because combining all 3 types, plus
# reference gets too long for the hint text
None: ['recipient'],
@@ -266,7 +264,7 @@ def get_status_filters(service, message_type, statistics):
stats = {
key: sum(
statistics[message_type][key]
- for message_type in {'email', 'sms', 'letter'}
+ for message_type in {'email', 'sms'}
)
for key in {'requested', 'delivered', 'failed'}
}
@@ -401,9 +399,6 @@ def get_preview_of_content(notification):
if notification['template'].get('redact_personalisation'):
notification['personalisation'] = {}
- if notification['template']['is_precompiled_letter']:
- return notification['client_reference']
-
if notification['template']['template_type'] == 'sms':
return str(SMSBodyPreviewTemplate(
notification['template'],
@@ -416,9 +411,3 @@ def get_preview_of_content(notification):
notification['personalisation'],
redact_missing_personalisation=True,
).subject)
-
- if notification['template']['template_type'] == 'letter':
- return Markup(LetterPreviewTemplate(
- notification['template'],
- notification['personalisation'],
- ).subject)
diff --git a/app/main/views/letter_branding.py b/app/main/views/letter_branding.py
deleted file mode 100644
index 4723d27cf..000000000
--- a/app/main/views/letter_branding.py
+++ /dev/null
@@ -1,177 +0,0 @@
-from botocore.exceptions import ClientError as BotoClientError
-from flask import (
- current_app,
- redirect,
- render_template,
- request,
- session,
- url_for,
-)
-from notifications_python_client.errors import HTTPError
-
-from app import letter_branding_client
-from app.main import main
-from app.main.forms import (
- AdminEditLetterBrandingForm,
- SearchByNameForm,
- SVGFileUpload,
-)
-from app.s3_client.s3_logo_client import (
- LETTER_TEMP_TAG,
- delete_letter_temp_file,
- delete_letter_temp_files_created_by,
- letter_filename_for_db,
- permanent_letter_logo_name,
- persist_logo,
- upload_letter_temp_logo,
-)
-from app.utils.user import user_is_platform_admin
-
-
-@main.route("/letter-branding", methods=['GET'])
-@user_is_platform_admin
-def letter_branding():
-
- brandings = letter_branding_client.get_all_letter_branding()
-
- return render_template(
- 'views/letter-branding/select-letter-branding.html',
- letter_brandings=brandings,
- search_form=SearchByNameForm()
- )
-
-
-@main.route("/letter-branding//edit", methods=['GET', 'POST'])
-@main.route("/letter-branding//edit/", methods=['GET', 'POST'])
-@user_is_platform_admin
-def update_letter_branding(branding_id, logo=None):
- letter_branding = letter_branding_client.get_letter_branding(branding_id)
-
- file_upload_form = SVGFileUpload()
- letter_branding_details_form = AdminEditLetterBrandingForm(
- name=letter_branding['name'],
- )
-
- file_upload_form_submitted = file_upload_form.file.data
- details_form_submitted = request.form.get('operation') == 'branding-details'
-
- logo = logo if logo else permanent_letter_logo_name(letter_branding['filename'], 'svg')
-
- if file_upload_form_submitted and file_upload_form.validate_on_submit():
- upload_filename = upload_letter_temp_logo(
- file_upload_form.file.data.filename,
- file_upload_form.file.data,
- user_id=session["user_id"]
- )
-
- if logo.startswith(LETTER_TEMP_TAG.format(user_id=session['user_id'])):
- delete_letter_temp_file(logo)
-
- return redirect(url_for('.update_letter_branding', branding_id=branding_id, logo=upload_filename))
-
- if details_form_submitted and letter_branding_details_form.validate_on_submit():
- db_filename = letter_filename_for_db(logo, session['user_id'])
-
- try:
- if db_filename == letter_branding['filename']:
-
- letter_branding_client.update_letter_branding(
- branding_id=branding_id,
- filename=db_filename,
- name=letter_branding_details_form.name.data,
- )
-
- return redirect(url_for('main.letter_branding'))
- else:
- letter_branding_client.update_letter_branding(
- branding_id=branding_id,
- filename=db_filename,
- name=letter_branding_details_form.name.data,
- )
-
- upload_letter_svg_logo(logo, db_filename, session['user_id'])
-
- return redirect(url_for('main.letter_branding'))
-
- except HTTPError as e:
- if 'name' in e.message:
- letter_branding_details_form.name.errors.append(e.message['name'][0])
- else:
- raise e
- except BotoClientError:
- # we had a problem saving the file - rollback the db changes
- letter_branding_client.update_letter_branding(
- branding_id=branding_id,
- filename=letter_branding['filename'],
- name=letter_branding['name'],
- )
- file_upload_form.file.errors = ['Error saving uploaded file - try uploading again']
-
- return render_template(
- 'views/letter-branding/manage-letter-branding.html',
- file_upload_form=file_upload_form,
- letter_branding_details_form=letter_branding_details_form,
- cdn_url=current_app.config['LOGO_CDN_DOMAIN'],
- logo=logo,
- is_update=True
- )
-
-
-@main.route("/letter-branding/create", methods=['GET', 'POST'])
-@main.route("/letter-branding/create/", methods=['GET', 'POST'])
-@user_is_platform_admin
-def create_letter_branding(logo=None):
- file_upload_form = SVGFileUpload()
- letter_branding_details_form = AdminEditLetterBrandingForm()
-
- file_upload_form_submitted = file_upload_form.file.data
- details_form_submitted = request.form.get('operation') == 'branding-details'
-
- if file_upload_form_submitted and file_upload_form.validate_on_submit():
- upload_filename = upload_letter_temp_logo(
- file_upload_form.file.data.filename,
- file_upload_form.file.data,
- user_id=session["user_id"]
- )
-
- if logo and logo.startswith(LETTER_TEMP_TAG.format(user_id=session['user_id'])):
- delete_letter_temp_file(logo)
-
- return redirect(url_for('.create_letter_branding', logo=upload_filename))
-
- if details_form_submitted and letter_branding_details_form.validate_on_submit():
- if logo:
- db_filename = letter_filename_for_db(logo, session['user_id'])
-
- try:
- letter_branding_client.create_letter_branding(
- filename=db_filename,
- name=letter_branding_details_form.name.data,
- )
-
- upload_letter_svg_logo(logo, db_filename, session['user_id'])
-
- return redirect(url_for('main.letter_branding'))
-
- except HTTPError as e:
- if 'name' in e.message:
- letter_branding_details_form.name.errors.append(e.message['name'][0])
- else:
- raise e
- else:
- # Show error on upload form if trying to submit with no logo
- file_upload_form.validate()
-
- return render_template(
- 'views/letter-branding/manage-letter-branding.html',
- file_upload_form=file_upload_form,
- letter_branding_details_form=letter_branding_details_form,
- cdn_url=current_app.config['LOGO_CDN_DOMAIN'],
- logo=logo
- )
-
-
-def upload_letter_svg_logo(old_filename, new_filename, user_id):
- persist_logo(old_filename, permanent_letter_logo_name(new_filename, 'svg'))
-
- delete_letter_temp_files_created_by(user_id)
diff --git a/app/main/views/notifications.py b/app/main/views/notifications.py
index 600e735f2..3b3d2b1fb 100644
--- a/app/main/views/notifications.py
+++ b/app/main/views/notifications.py
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from datetime import datetime
-from dateutil import parser
from flask import (
Response,
jsonify,
@@ -10,11 +9,6 @@ from flask import (
stream_with_context,
url_for,
)
-from notifications_utils import LETTER_MAX_PAGE_COUNT
-from notifications_utils.letter_timings import (
- get_letter_timings,
- letter_can_be_cancelled,
-)
from app import (
current_service,
@@ -24,7 +18,6 @@ from app import (
)
from app.main import main
from app.notify_client.api_key_api_client import KEY_TYPE_TEST
-from app.template_previews import get_page_count_for_letter
from app.utils import (
DELIVERED_STATUSES,
FAILURE_STATUSES,
@@ -33,7 +26,6 @@ from app.utils import (
set_status_filters,
)
from app.utils.csv import generate_notifications_csv
-from app.utils.letters import get_letter_validation_error
from app.utils.templates import get_template
from app.utils.user import user_has_permissions
@@ -46,30 +38,10 @@ def view_notification(service_id, notification_id):
personalisation = get_all_personalisation_from_notification(notification)
error_message = None
- page_count = get_page_count_for_letter(notification['template'], values=personalisation)
- if page_count and page_count > LETTER_MAX_PAGE_COUNT:
- # We check page count here to show the right error message for a letter that is too long.
- # Another way to do this would be to get the status and error message from letter metadata.
- # This would be a significant amount of work though, out of scope for this bug fix.
- # This is because currently we do not pull the letter from S3 when showing preview.
- # Instead, we generate letter preview based on the letter template and personalisation.
- # Additionally, when a templated letter is sent via the api and the personalisation pushes the
- # page count over 10 pages, it takes a while for validation status to come through.
- # Checking page count here will enable us to show the error message even if the letter is not
- # fully processed yet.
- error_message = get_letter_validation_error(
- "letter-too-long", [1], page_count
- )
- if notification.get('postage'):
- if notification["status"] == "validation-failed":
- notification['template']['postage'] = None
- else:
- notification['template']['postage'] = notification['postage']
template = get_template(
notification['template'],
current_service,
- page_count=page_count,
show_recipient=True,
redact_missing_personalisation=True,
sms_sender=notification['reply_to_text'],
@@ -81,11 +53,6 @@ def view_notification(service_id, notification_id):
else:
job = None
- notification_created = parser.parse(notification['created_at']).replace(tzinfo=None)
-
- show_cancel_button = notification['notification_type'] == 'letter' and \
- letter_can_be_cancelled(notification['status'], notification_created)
-
if get_help_argument() or request.args.get('help') == '0':
# help=0 is set when you’ve just sent a notification. We
# only want to show the back link when you’ve navigated to a
@@ -105,14 +72,6 @@ def view_notification(service_id, notification_id):
status='sending,delivered,failed',
)
- if notification['notification_type'] == 'letter':
- estimated_letter_delivery_date = get_letter_timings(
- notification['created_at'],
- postage=notification['postage']
- ).earliest_delivery
- else:
- estimated_letter_delivery_date = None
-
return render_template(
'views/notifications/notification.html',
finished=(notification['status'] in (DELIVERED_STATUSES + FAILURE_STATUSES)),
@@ -133,12 +92,8 @@ def view_notification(service_id, notification_id):
created_at=notification['created_at'],
updated_at=notification['updated_at'],
help=get_help_argument(),
- estimated_letter_delivery_date=estimated_letter_delivery_date,
notification_id=notification['id'],
- postage=notification['postage'],
can_receive_inbound=(current_service.has_permission('inbound_sms')),
- is_precompiled_letter=notification['template']['is_precompiled_letter'],
- show_cancel_button=show_cancel_button,
sent_with_test_key=(
notification.get('key_type') == KEY_TYPE_TEST
),
diff --git a/app/main/views/organisations.py b/app/main/views/organisations.py
index ea176ce94..fbe5f6fcd 100644
--- a/app/main/views/organisations.py
+++ b/app/main/views/organisations.py
@@ -9,7 +9,6 @@ from notifications_python_client.errors import HTTPError
from app import (
current_organisation,
email_branding_client,
- letter_branding_client,
org_invite_api_client,
organisations_client,
)
@@ -22,7 +21,6 @@ from app.main.forms import (
AdminOrganisationGoLiveNotesForm,
AdminPreviewBrandingForm,
AdminSetEmailBrandingForm,
- AdminSetLetterBrandingForm,
InviteOrgUserForm,
OrganisationAgreementSignedForm,
OrganisationCrownStatusForm,
@@ -96,7 +94,7 @@ def organisation_dashboard(org_id):
search_form=SearchByNameForm() if len(services) > 7 else None,
**{
f'total_{key}': sum(service[key] for service in services)
- for key in ('emails_sent', 'sms_cost', 'letter_cost')
+ for key in ('emails_sent', 'sms_cost')
},
download_link=url_for(
'.download_organisation_usage_report',
@@ -123,7 +121,6 @@ def download_organisation_usage_report(org_id):
monetary_column_names = OrderedDict([
('sms_cost', 'Spent on text messages ($)'),
- ('letter_cost', 'Spent on letters ($)')
])
org_usage_data = [
@@ -386,51 +383,6 @@ def organisation_preview_email_branding(org_id):
)
-@main.route("/organisations//settings/set-letter-branding", methods=['GET', 'POST'])
-@user_is_platform_admin
-def edit_organisation_letter_branding(org_id):
- letter_branding = letter_branding_client.get_all_letter_branding()
-
- form = AdminSetLetterBrandingForm(
- all_branding_options=get_branding_as_value_and_label(letter_branding),
- current_branding=current_organisation.letter_branding_id,
- )
-
- if form.validate_on_submit():
- return redirect(url_for(
- '.organisation_preview_letter_branding',
- org_id=org_id,
- branding_style=form.branding_style.data,
- ))
-
- return render_template(
- 'views/organisations/organisation/settings/set-letter-branding.html',
- form=form,
- search_form=SearchByNameForm()
- )
-
-
-@main.route("/organisations//settings/preview-letter-branding", methods=['GET', 'POST'])
-@user_is_platform_admin
-def organisation_preview_letter_branding(org_id):
- branding_style = request.args.get('branding_style')
-
- form = AdminPreviewBrandingForm(branding_style=branding_style)
-
- if form.validate_on_submit():
- current_organisation.update(
- letter_branding_id=form.branding_style.data,
- delete_services_cache=True,
- )
- return redirect(url_for('.organisation_settings', org_id=org_id))
-
- return render_template(
- 'views/organisations/organisation/settings/preview-letter-branding.html',
- form=form,
- action=url_for('main.organisation_preview_letter_branding', org_id=org_id),
- )
-
-
@main.route("/organisations//settings/edit-organisation-domains", methods=['GET', 'POST'])
@user_is_platform_admin
def edit_organisation_domains(org_id):
diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py
index c86436d31..fd9a2a863 100644
--- a/app/main/views/platform_admin.py
+++ b/app/main/views/platform_admin.py
@@ -1,16 +1,14 @@
import itertools
-import re
from collections import OrderedDict
from datetime import datetime
-from flask import abort, flash, redirect, render_template, request, url_for
+from flask import abort, flash, render_template, request, url_for
from notifications_python_client.errors import HTTPError
from app import (
billing_api_client,
complaint_api_client,
format_date_numeric,
- letter_jobs_client,
notification_api_client,
platform_stats_api_client,
service_api_client,
@@ -19,7 +17,6 @@ from app.extensions import redis_client
from app.main import main
from app.main.forms import (
AdminClearCacheForm,
- AdminReturnedLettersForm,
BillingReportDateFilterForm,
DateFilterForm,
RequiredDateFilterForm,
@@ -137,22 +134,6 @@ def make_columns(global_stats, complaints_number):
'label': 'test text messages'
}
},
- # letter
- {
- 'black_box': {
- 'number': global_stats['letter']['total'],
- 'notification_type': 'letter'
- },
- 'other_data': [
- get_tech_failure_status_box_data(global_stats['letter']),
- get_status_box_data(global_stats['letter'],
- 'virus-scan-failed', 'virus scan failures', ZERO_FAILURE_THRESHOLD)
- ],
- 'test_data': {
- 'number': global_stats['letter']['test-key'],
- 'label': 'test letters'
- }
- },
]
@@ -222,10 +203,8 @@ def live_services_csv():
('live_date', 'Live date'),
('sms_volume_intent', 'SMS volume intent'),
('email_volume_intent', 'Email volume intent'),
- ('letter_volume_intent', 'Letter volume intent'),
('sms_totals', 'SMS sent this year'),
('email_totals', 'Emails sent this year'),
- ('letter_totals', 'Letters sent this year'),
('free_sms_fragment_limit', 'Free sms allowance'),
])
@@ -280,7 +259,7 @@ def get_billing_report():
end_date = form.end_date.data
headers = [
"organisation_id", "organisation_name", "service_id", "service_name",
- "sms_cost", "sms_chargeable_units", "total_letters", "letter_cost", "letter_breakdown",
+ "sms_cost", "sms_chargeable_units",
"purchase_order_number", "contact_names", "contact_email_addresses", "billing_reference"
]
try:
@@ -295,8 +274,8 @@ def get_billing_report():
rows = [
[
r["organisation_id"], r["organisation_name"], r["service_id"], r["service_name"],
- r["sms_cost"], r["sms_chargeable_units"], r["total_letters"], r["letter_cost"],
- r["letter_breakdown"].strip(), r.get("purchase_order_number"), r.get("contact_names"),
+ r["sms_cost"], r["sms_chargeable_units"],
+ r.get("purchase_order_number"), r.get("contact_names"),
r.get("contact_email_addresses"), r.get("billing_reference")
]
for r in result
@@ -324,7 +303,6 @@ def get_volumes_by_service():
headers = [
"organisation id", "organisation name", "service id", "service name",
"free allowance", "sms notifications", "sms chargeable units", "email totals",
- "letter totals", "letter cost", "letter sheet totals"
]
result = billing_api_client.get_data_for_volumes_by_service_report(start_date, end_date)
@@ -332,7 +310,6 @@ def get_volumes_by_service():
[
r["organisation_id"], r["organisation_name"], r["service_id"], r["service_name"],
r["free_allowance"], r["sms_notifications"], r["sms_chargeable_units"], r["email_totals"],
- r["letter_totals"], r["letter_cost"], r["letter_sheet_totals"]
]
for r in result
]
@@ -358,14 +335,14 @@ def get_daily_volumes():
end_date = form.end_date.data
headers = [
"day", "sms totals", "sms fragment totals", "sms chargeable units",
- "email totals", "letter totals", "letter sheet totals"
+ "email totals",
]
result = billing_api_client.get_data_for_daily_volumes_report(start_date, end_date)
rows = [
[
r["day"], r["sms_totals"], r["sms_fragment_totals"], r["sms_chargeable_units"],
- r["email_totals"], r["letter_totals"], r["letter_sheet_totals"]
+ r["email_totals"],
]
for r in result
]
@@ -446,46 +423,6 @@ def platform_admin_list_complaints():
)
-@main.route("/platform-admin/returned-letters", methods=["GET", "POST"])
-@user_is_platform_admin
-def platform_admin_returned_letters():
- form = AdminReturnedLettersForm()
-
- if form.validate_on_submit():
- references = [
- re.sub('NOTIFY00[0-9]', '', r.strip())
- for r in form.references.data.split('\n')
- if r.strip()
- ]
-
- try:
- letter_jobs_client.submit_returned_letters(references)
- redis_client.delete_by_pattern(
- 'service-????????-????-????-????-????????????-returned-letters-statistics'
- )
- redis_client.delete_by_pattern(
- 'service-????????-????-????-????-????????????-returned-letters-summary'
- )
- except HTTPError as error:
- if error.status_code == 400:
- error_references = [
- re.match('references (.*) does not match', e['message']).group(1)
- for e in error.message
- ]
- form.references.errors.append("Invalid references: {}".format(', '.join(error_references)))
- else:
- raise error
- else:
- flash('Submitted {} letter references'.format(len(references)), 'default')
- return redirect(
- url_for('.platform_admin_returned_letters')
- )
- return render_template(
- 'views/platform-admin/returned-letters.html',
- form=form,
- )
-
-
@main.route("/platform-admin/clear-cache", methods=['GET', 'POST'])
@user_is_platform_admin
def clear_cache():
@@ -500,8 +437,6 @@ def clear_cache():
'service-????????-????-????-????-????????????-templates',
'service-????????-????-????-????-????????????-data-retention',
'service-????????-????-????-????-????????????-template-folders',
- 'service-????????-????-????-????-????????????-returned-letters-statistics',
- 'service-????????-????-????-????-????????????-returned-letters-summary',
]),
('template', [
'service-????????-????-????-????-????????????-templates',
@@ -512,10 +447,6 @@ def clear_cache():
'email_branding',
'email_branding-????????-????-????-????-????????????',
]),
- ('letter_branding', [
- 'letter_branding',
- 'letter_branding-????????-????-????-????-????????????',
- ]),
('organisation', [
'organisations',
'domains',
@@ -588,14 +519,9 @@ def create_global_stats(services):
'failed': 0,
'requested': 0
},
- 'letter': {
- 'delivered': 0,
- 'failed': 0,
- 'requested': 0
- }
}
for service in services:
- for msg_type, status in itertools.product(('sms', 'email', 'letter'), ('delivered', 'failed', 'requested')):
+ for msg_type, status in itertools.product(('sms', 'email'), ('delivered', 'failed', 'requested')):
stats[msg_type][status] += service['statistics'][msg_type][status]
for stat in stats.values():
diff --git a/app/main/views/returned_letters.py b/app/main/views/returned_letters.py
deleted file mode 100644
index ba222a066..000000000
--- a/app/main/views/returned_letters.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from collections import OrderedDict
-
-from flask import render_template
-
-from app import current_service, service_api_client
-from app.main import main
-from app.models.spreadsheet import Spreadsheet
-from app.utils.user import user_has_permissions
-
-
-@main.route("/services//returned-letters")
-@user_has_permissions('view_activity')
-def returned_letter_summary(service_id):
- return render_template(
- 'views/returned-letter-summary.html',
- data=current_service.returned_letter_summary,
- )
-
-
-@main.route("/services//returned-letters/")
-@user_has_permissions('view_activity')
-def returned_letters(service_id, reported_at):
-
- page_size = 50
- returned_letters = service_api_client.get_returned_letters(service_id, reported_at)
- count_of_returned_letters = len(returned_letters)
-
- return render_template(
- 'views/returned-letters.html',
- returned_letters=returned_letters[:page_size],
- reported_at=reported_at,
- more_than_one_page=(count_of_returned_letters > page_size),
- page_size=page_size,
- count_of_returned_letters=count_of_returned_letters,
- )
-
-
-@main.route("/services//returned-letters/.csv")
-@user_has_permissions('view_activity')
-def returned_letters_report(service_id, reported_at):
- returned_letters = service_api_client.get_returned_letters(service_id, reported_at)
- column_names = OrderedDict([
- ('notification_id', 'Notification ID'),
- ('client_reference', 'Reference'),
- ('created_at', 'Date sent'),
- ('email_address', 'Sent by'),
- ('template_name', 'Template name'),
- ('template_id', 'Template ID'),
- ('template_version', 'Template version'),
- ('original_file_name', 'Spreadsheet file name'),
- ('job_row_number', 'Spreadsheet row number'),
- ('uploaded_letter_file_name', 'Uploaded letter file name')
- ])
-
- # initialise with header row
- data = [[x for x in column_names.values()]]
-
- for row in returned_letters:
- data.append([row[key] for key in column_names.keys()])
-
- return Spreadsheet.from_rows(data).as_csv_data, 200, {
- 'Content-Type': 'text/csv; charset=utf-8',
- 'Content-Disposition': 'inline; filename="{} returned letters.csv"'.format(reported_at)
- }
diff --git a/app/main/views/send.py b/app/main/views/send.py
index 68f4813ea..fa95e3ba7 100644
--- a/app/main/views/send.py
+++ b/app/main/views/send.py
@@ -14,13 +14,8 @@ from flask import (
)
from flask_login import current_user
from notifications_python_client.errors import HTTPError
-from notifications_utils import LETTER_MAX_PAGE_COUNT, SMS_CHAR_COUNT_LIMIT
+from notifications_utils import SMS_CHAR_COUNT_LIMIT
from notifications_utils.insensitive_dict import InsensitiveDict
-from notifications_utils.pdf import is_letter_too_long
-from notifications_utils.postal_address import (
- PostalAddress,
- address_lines_1_to_7_keys,
-)
from notifications_utils.recipients import RecipientCSV, first_column_headings
from notifications_utils.sanitise_text import SanitiseASCII
from xlrd.biffh import XLRDError
@@ -37,7 +32,6 @@ from app.main import main, no_cookie
from app.main.forms import (
ChooseTimeForm,
CsvUploadForm,
- LetterAddressForm,
SetSenderForm,
get_placeholder_form_instance,
)
@@ -49,7 +43,7 @@ from app.s3_client.s3_csv_client import (
s3upload,
set_metadata_on_csv_upload,
)
-from app.template_previews import TemplatePreview, get_page_count_for_letter
+from app.template_previews import TemplatePreview
from app.utils import (
PermanentRedirect,
should_skip_template_page,
@@ -59,11 +53,6 @@ from app.utils.csv import Spreadsheet, get_errors_for_csv
from app.utils.templates import get_template
from app.utils.user import user_has_permissions
-letter_address_columns = [
- column.replace('_', ' ')
- for column in address_lines_1_to_7_keys
-]
-
def get_example_csv_fields(column_headers, use_example_as_example, submitted_fields):
if use_example_as_example:
@@ -78,12 +67,6 @@ def get_example_csv_rows(template, use_example_as_example=True, submitted_fields
return {
'email': ['test@example.com'] if use_example_as_example else [current_user.email_address],
'sms': ['12223334444'] if use_example_as_example else [current_user.mobile_number],
- 'letter': [
- (submitted_fields or {}).get(
- key, get_example_letter_address(key) if use_example_as_example else key
- )
- for key in letter_address_columns
- ]
}[template.template_type] + get_example_csv_fields(
(
placeholder for placeholder in template.placeholders
@@ -96,14 +79,6 @@ def get_example_csv_rows(template, use_example_as_example=True, submitted_fields
)
-def get_example_letter_address(key):
- return {
- 'address line 1': 'A. Name',
- 'address line 2': '123 Example Street',
- 'address line 3': 'XM4 5HQ'
- }.get(key, '')
-
-
@main.route("/services//send//csv", methods=['GET', 'POST'])
@user_has_permissions('send_messages', restrict_admin_usage=True)
def send_messages(service_id, template_id):
@@ -130,13 +105,6 @@ def send_messages(service_id, template_id):
db_template,
current_service,
show_recipient=True,
- letter_preview_url=url_for(
- 'no_cookie.view_letter_template_preview',
- service_id=service_id,
- template_id=template_id,
- filetype='png',
- page_count=get_page_count_for_letter(db_template),
- ),
email_reply_to=email_reply_to,
sms_sender=sms_sender,
)
@@ -217,9 +185,6 @@ def set_sender(service_id, template_id):
template = current_service.get_template_with_user_permission_or_403(template_id, current_user)
- if template['template_type'] == 'letter':
- return redirect_to_one_off
-
sender_details = get_sender_details(service_id, template['template_type'])
if len(sender_details) == 1:
@@ -272,11 +237,6 @@ def get_sender_context(sender_details, template_type):
'description': 'Where should replies come back to?',
'field_name': 'email_address'
},
- 'letter': {
- 'title': 'Send to one recipient',
- 'description': 'What should appear in the top right of the letter?',
- 'field_name': 'contact_block'
- },
'sms': {
'title': 'Who should the message come from?',
'description': 'Who should the message come from?',
@@ -301,7 +261,6 @@ def get_sender_context(sender_details, template_type):
def get_sender_details(service_id, template_type):
api_call = {
'email': service_api_client.get_reply_to_email_addresses,
- 'letter': service_api_client.get_letter_contacts,
'sms': service_api_client.get_sms_senders
}[template_type]
return api_call(service_id)
@@ -314,11 +273,6 @@ def send_one_off(service_id, template_id):
session['placeholders'] = {}
db_template = current_service.get_template_with_user_permission_or_403(template_id, current_user)
- if db_template['template_type'] == 'letter':
- session['sender_id'] = None
- return redirect(
- url_for('.send_one_off_letter_address', service_id=service_id, template_id=template_id)
- )
if db_template['template_type'] not in current_service.available_template_types:
return redirect(url_for(
@@ -344,72 +298,6 @@ def get_notification_check_endpoint(service_id, template):
))
-@main.route(
- "/services//send//one-off/address",
- methods=['GET', 'POST']
-)
-@user_has_permissions('send_messages', restrict_admin_usage=True)
-def send_one_off_letter_address(service_id, template_id):
- if {'recipient', 'placeholders'} - set(session.keys()):
- # if someone has come here via a bookmark or back button they might have some stuff still in their session
- return redirect(url_for('.send_one_off', service_id=service_id, template_id=template_id))
-
- db_template = current_service.get_template_with_user_permission_or_403(template_id, current_user)
-
- session_placeholders = get_normalised_placeholders_from_session()
-
- template = get_template(
- db_template,
- current_service,
- show_recipient=True,
- letter_preview_url=url_for(
- 'no_cookie.send_test_preview',
- service_id=service_id,
- template_id=template_id,
- filetype='png',
- ),
- page_count=get_page_count_for_letter(db_template, session_placeholders),
- email_reply_to=None,
- sms_sender=None
- )
-
- current_session_address = PostalAddress.from_personalisation(session_placeholders)
-
- form = LetterAddressForm(
- address=current_session_address.normalised,
- allow_international_letters=current_service.has_permission('international_letters'),
- )
-
- if form.validate_on_submit():
- session['placeholders'].update(PostalAddress(form.address.data).as_personalisation)
-
- placeholders = fields_to_fill_in(template)
- if all_placeholders_in_session(placeholders):
- return get_notification_check_endpoint(service_id, template)
-
- first_non_address_placeholder_index = len(address_lines_1_to_7_keys)
-
- return redirect(url_for(
- 'main.send_one_off_step',
- service_id=service_id,
- template_id=template_id,
- step_index=first_non_address_placeholder_index,
- ))
-
- return render_template(
- 'views/send-one-off-letter-address.html',
- page_title=get_send_test_page_title(
- template_type='letter',
- entering_recipient=True,
- name=template.name,
- ),
- template=template,
- form=form,
- back_link=get_back_link(service_id, template, 0),
- link_to_upload=True,
- )
-
-
@main.route(
"/services//send//one-off/step-",
methods=['GET', 'POST'],
@@ -438,13 +326,6 @@ def send_one_off_step(service_id, template_id, step_index):
db_template,
current_service,
show_recipient=True,
- letter_preview_url=url_for(
- 'no_cookie.send_test_preview',
- service_id=service_id,
- template_id=template_id,
- filetype='png',
- ),
- page_count=get_page_count_for_letter(db_template, values=template_values),
email_reply_to=email_reply_to,
sms_sender=sms_sender
)
@@ -462,22 +343,6 @@ def send_one_off_step(service_id, template_id, step_index):
template_id=template_id,
))
- # if we're in a letter, we should show address block rather than "address line #" or "postcode"
- if template.template_type == 'letter':
- if step_index < len(address_lines_1_to_7_keys):
- return redirect(url_for(
- '.send_one_off_letter_address',
- service_id=service_id,
- template_id=template_id,
- ))
- if current_placeholder in InsensitiveDict(PostalAddress('').as_personalisation):
- return redirect(url_for(
- request.endpoint,
- service_id=service_id,
- template_id=template_id,
- step_index=step_index + 1,
- ))
-
form = get_placeholder_form_instance(
current_placeholder,
dict_to_populate_from=get_normalised_placeholders_from_session(),
@@ -487,12 +352,8 @@ def send_one_off_step(service_id, template_id, step_index):
if form.validate_on_submit():
# if it's the first input (phone/email), we store against `recipient` as well, for easier extraction.
- # Only if it's not a letter.
- # And only if we're not on the test route, since that will already have the user's own number set
- if (
- step_index == 0
- and template.template_type != 'letter'
- ):
+ # Only if we're not on the test route, since that will already have the user's own number set
+ if step_index == 0:
session['recipient'] = form.placeholder_value.data
session['placeholders'][current_placeholder] = form.placeholder_value.data
@@ -542,12 +403,6 @@ def send_test_preview(service_id, template_id, filetype):
template = get_template(
db_template,
current_service,
- letter_preview_url=url_for(
- 'no_cookie.send_test_preview',
- service_id=service_id,
- template_id=template_id,
- filetype='png',
- ),
)
template.values = get_normalised_placeholders_from_session()
@@ -596,7 +451,7 @@ def send_from_contact_list(service_id, template_id, contact_list_id):
))
-def _check_messages(service_id, template_id, upload_id, preview_row, letters_as_pdf=False):
+def _check_messages(service_id, template_id, upload_id, preview_row):
try:
# The happy path is that the job doesn’t already exist, so the
# API will return a 404 and the client will raise HTTPError.
@@ -633,20 +488,8 @@ def _check_messages(service_id, template_id, upload_id, preview_row, letters_as_
db_template,
current_service,
show_recipient=True,
- letter_preview_url=url_for(
- 'no_cookie.check_messages_preview',
- service_id=service_id,
- template_id=template_id,
- upload_id=upload_id,
- filetype='png',
- row_index=preview_row,
- ) if not letters_as_pdf else None,
email_reply_to=email_reply_to,
sms_sender=sms_sender,
- # In this case, we don't provide template values when calculating the page count
- # because we don't know them at this point. It means that later on we will need to
- # recalculate the page count once we have the values
- page_count=get_page_count_for_letter(db_template),
)
recipients = RecipientCSV(
contents,
@@ -658,11 +501,10 @@ def _check_messages(service_id, template_id, upload_id, preview_row, letters_as_
) if current_service.trial_mode else None,
remaining_messages=remaining_messages,
allow_international_sms=current_service.has_permission('international_sms'),
- allow_international_letters=current_service.has_permission('international_letters'),
)
if request.args.get('from_test'):
- # only happens if generating a letter preview test
+ # TODO: may not be required after letters code removed
back_link = url_for('main.send_one_off', service_id=service_id, template_id=template.id)
choose_time_form = None
else:
@@ -677,9 +519,6 @@ def _check_messages(service_id, template_id, upload_id, preview_row, letters_as_
elif preview_row > 2:
abort(404)
- page_count = get_page_count_for_letter(db_template, template.values)
- template.page_count = page_count
-
original_file_name = get_csv_metadata(service_id, upload_id).get('original_file_name', '')
return dict(
@@ -695,20 +534,11 @@ def _check_messages(service_id, template_id, upload_id, preview_row, letters_as_
remaining_messages=remaining_messages,
choose_time_form=choose_time_form,
back_link=back_link,
- trying_to_send_letters_in_trial_mode=all((
- current_service.trial_mode,
- template.template_type == 'letter',
- )),
first_recipient_column=recipients.recipient_column_headers[0],
preview_row=preview_row,
sent_previously=job_api_client.has_sent_previously(
service_id, template.id, db_template['version'], original_file_name
),
- letter_too_long=is_letter_too_long(page_count),
- letter_max_pages=LETTER_MAX_PAGE_COUNT,
- letter_min_address_lines=PostalAddress.MIN_LINES,
- letter_max_address_lines=PostalAddress.MAX_LINES,
- page_count=page_count
)
@@ -737,7 +567,6 @@ def check_messages(service_id, template_id, upload_id, row_index=2):
if (
data['errors']
- or data['trying_to_send_letters_in_trial_mode']
):
return render_template('views/check/column-errors.html', **data)
@@ -748,8 +577,7 @@ def check_messages(service_id, template_id, upload_id, row_index=2):
'original_file_name': data.get('original_file_name', ''),
}
- if session.get('sender_id') and data['template'].template_type != 'letter':
- # sender_id is not an option for sending letters.
+ if session.get('sender_id'):
metadata_kwargs['sender_id'] = session['sender_id']
set_metadata_on_csv_upload(service_id, upload_id, **metadata_kwargs)
@@ -767,6 +595,7 @@ def check_messages(service_id, template_id, upload_id, row_index=2):
)
@user_has_permissions('send_messages')
def check_messages_preview(service_id, template_id, upload_id, filetype, row_index=2):
+ # TODO: likely candidate for deletion
if filetype == 'pdf':
page = None
elif filetype == 'png':
@@ -775,7 +604,7 @@ def check_messages_preview(service_id, template_id, upload_id, filetype, row_ind
abort(404)
template = _check_messages(
- service_id, template_id, upload_id, row_index, letters_as_pdf=True
+ service_id, template_id, upload_id, row_index
)['template']
return TemplatePreview.from_utils_template(template, filetype, page=page)
@@ -823,9 +652,6 @@ def start_job(service_id, upload_id):
def fields_to_fill_in(template, prefill_current_user=False):
- if 'letter' == template.template_type:
- return letter_address_columns + list(template.placeholders)
-
if not prefill_current_user:
return first_column_headings[template.template_type] + list(template.placeholders)
@@ -881,20 +707,6 @@ def get_back_link(service_id, template, step_index, placeholders=None):
template_id=template.id,
)
- if template.template_type == 'letter' and placeholders:
- # Make sure we’re not redirecting users to a page which will
- # just redirect them forwards again
- back_link_destination_step_index = next((
- index
- for index, placeholder in reversed(
- list(enumerate(placeholders[:step_index]))
- )
- if placeholder not in InsensitiveDict(
- PostalAddress('').as_personalisation
- )
- ), 1)
- return get_back_link(service_id, template, back_link_destination_step_index + 1)
-
return url_for(
'main.send_one_off_step',
service_id=service_id,
@@ -964,13 +776,6 @@ def _check_notification(service_id, template_id, exception=None):
show_recipient=True,
email_reply_to=email_reply_to,
sms_sender=sms_sender,
- letter_preview_url=url_for(
- 'no_cookie.check_notification_preview',
- service_id=service_id,
- template_id=template_id,
- filetype='png',
- ),
- page_count=get_page_count_for_letter(db_template),
)
placeholders = fields_to_fill_in(template)
@@ -980,21 +785,15 @@ def _check_notification(service_id, template_id, exception=None):
if (
(
not session.get('recipient')
- and db_template['template_type'] != 'letter'
)
or not all_placeholders_in_session(template.placeholders)
):
raise PermanentRedirect(back_link)
template.values = get_recipient_and_placeholders_from_session(template.template_type)
- page_count = get_page_count_for_letter(db_template, template.values)
- template.page_count = page_count
return dict(
template=template,
back_link=back_link,
- letter_too_long=is_letter_too_long(page_count),
- letter_max_pages=LETTER_MAX_PAGE_COUNT,
- page_count=page_count,
**(get_template_error_dict(exception) if exception else {}),
)
@@ -1086,11 +885,7 @@ def get_sms_sender_from_session():
def get_spreadsheet_column_headings_from_template(template):
column_headings = []
- if template.template_type == 'letter':
- # We want to avoid showing `address line 7` for now
- recipient_columns = letter_address_columns
- else:
- recipient_columns = first_column_headings[template.template_type]
+ recipient_columns = first_column_headings[template.template_type]
for column_heading in (
recipient_columns + list(template.placeholders)
diff --git a/app/main/views/service_settings.py b/app/main/views/service_settings.py
index f325aae29..96382cdcc 100644
--- a/app/main/views/service_settings.py
+++ b/app/main/views/service_settings.py
@@ -22,7 +22,6 @@ from app import (
current_service,
email_branding_client,
inbound_number_client,
- letter_branding_client,
notification_api_client,
organisations_client,
service_api_client,
@@ -46,16 +45,13 @@ from app.main.forms import (
AdminServiceRateLimitForm,
AdminServiceSMSAllowanceForm,
AdminSetEmailBrandingForm,
- AdminSetLetterBrandingForm,
AdminSetOrganisationForm,
ChooseEmailBrandingForm,
- ChooseLetterBrandingForm,
EstimateUsageForm,
RenameServiceForm,
SearchByNameForm,
ServiceContactDetailsForm,
ServiceEditInboundNumberForm,
- ServiceLetterContactBlockForm,
ServiceOnOffSettingForm,
ServiceReplyToEmailForm,
ServiceSmsSenderForm,
@@ -76,7 +72,6 @@ from app.utils.user import (
PLATFORM_ADMIN_SERVICE_PERMISSIONS = OrderedDict([
('inbound_sms', {'title': 'Receive inbound SMS', 'requires': 'sms', 'endpoint': '.service_set_inbound_number'}),
('email_auth', {'title': 'Email authentication'}),
- ('international_letters', {'title': 'Send international letters', 'requires': 'letter'}),
])
@@ -132,7 +127,6 @@ def estimate_usage(service_id):
form = EstimateUsageForm(
volume_email=current_service.volume_email,
volume_sms=current_service.volume_sms,
- volume_letter=current_service.volume_letter,
consent_to_research={
True: 'yes',
False: 'no',
@@ -143,7 +137,6 @@ def estimate_usage(service_id):
current_service.update(
volume_email=form.volume_email.data,
volume_sms=form.volume_sms.data,
- volume_letter=form.volume_letter.data,
consent_to_research=(form.consent_to_research.data == 'yes'),
)
return redirect(url_for(
@@ -607,27 +600,6 @@ def service_set_international_sms(service_id):
)
-@main.route("/services//service-settings/set-international-letters", methods=['GET', 'POST'])
-@user_has_permissions('manage_service')
-def service_set_international_letters(service_id):
- form = ServiceOnOffSettingForm(
- 'Send letters to international addresses',
- enabled=current_service.has_permission('international_letters'),
- )
- if form.validate_on_submit():
- current_service.force_permission(
- 'international_letters',
- on=form.enabled.data,
- )
- return redirect(
- url_for(".service_settings", service_id=service_id)
- )
- return render_template(
- 'views/service-settings/set-international-letters.html',
- form=form,
- )
-
-
@main.route("/services//service-settings/set-inbound-sms", methods=['GET'])
@user_has_permissions('manage_service')
def service_set_inbound_sms(service_id):
@@ -636,24 +608,11 @@ def service_set_inbound_sms(service_id):
)
-@main.route("/services//service-settings/set-letters", methods=['GET'])
-@user_has_permissions('manage_service')
-def service_set_letters(service_id):
- return redirect(
- url_for(
- '.service_set_channel',
- service_id=current_service.id,
- channel='letter',
- ),
- code=301,
- )
-
-
@main.route("/services//service-settings/set-", methods=['GET', 'POST'])
@user_has_permissions('manage_service')
def service_set_channel(service_id, channel):
- if channel not in {'email', 'sms', 'letter'}:
+ if channel not in {'email', 'sms'}:
abort(404)
form = ServiceSwitchChannelForm(
@@ -684,103 +643,6 @@ def service_set_auth_type(service_id):
)
-@main.route("/services//service-settings/letter-contacts", methods=['GET'])
-@user_has_permissions('manage_service', 'manage_api_keys')
-def service_letter_contact_details(service_id):
- letter_contact_details = service_api_client.get_letter_contacts(service_id)
- return render_template(
- 'views/service-settings/letter-contact-details.html',
- letter_contact_details=letter_contact_details)
-
-
-@main.route("/services//service-settings/letter-contact/add", methods=['GET', 'POST'])
-@user_has_permissions('manage_service')
-def service_add_letter_contact(service_id):
- form = ServiceLetterContactBlockForm()
- first_contact_block = current_service.count_letter_contact_details == 0
- from_template = request.args.get('from_template')
- if form.validate_on_submit():
- new_letter_contact = service_api_client.add_letter_contact(
- current_service.id,
- contact_block=form.letter_contact_block.data.replace('\r', '') or None,
- is_default=first_contact_block if first_contact_block else form.is_default.data
- )
- if from_template:
- service_api_client.update_service_template_sender(
- service_id,
- from_template,
- new_letter_contact['data']['id'],
- )
- return redirect(
- url_for('.view_template', service_id=service_id, template_id=from_template)
- )
- return redirect(url_for('.service_letter_contact_details', service_id=service_id))
- return render_template(
- 'views/service-settings/letter-contact/add.html',
- form=form,
- first_contact_block=first_contact_block,
- back_link=(
- url_for('main.view_template', template_id=from_template, service_id=current_service.id)
- if from_template
- else url_for('.service_letter_contact_details', service_id=current_service.id)
- ),
- )
-
-
-@main.route(
- "/services//service-settings/letter-contact//edit",
- methods=['GET', 'POST'],
- endpoint="service_edit_letter_contact",
-)
-@main.route(
- "/services//service-settings/letter-contact//delete",
- methods=['GET'],
- endpoint="service_confirm_delete_letter_contact",
-)
-@user_has_permissions('manage_service')
-def service_edit_letter_contact(service_id, letter_contact_id):
- letter_contact_block = current_service.get_letter_contact_block(letter_contact_id)
- form = ServiceLetterContactBlockForm(
- letter_contact_block=letter_contact_block['contact_block']
- )
- if request.method == 'GET':
- form.is_default.data = letter_contact_block['is_default']
- if form.validate_on_submit():
- current_service.edit_letter_contact_block(
- id=letter_contact_id,
- contact_block=form.letter_contact_block.data.replace('\r', '') or None,
- is_default=letter_contact_block['is_default'] or form.is_default.data
- )
- return redirect(url_for('.service_letter_contact_details', service_id=service_id))
-
- if (request.endpoint == "main.service_confirm_delete_letter_contact"):
- flash("Are you sure you want to delete this contact block?", 'delete')
- return render_template(
- 'views/service-settings/letter-contact/edit.html',
- form=form,
- letter_contact_id=letter_contact_block['id'])
-
-
-@main.route("/services//service-settings/letter-contact/make-blank-default")
-@user_has_permissions('manage_service')
-def service_make_blank_default_letter_contact(service_id):
- current_service.remove_default_letter_contact_block()
- return redirect(url_for('.service_letter_contact_details', service_id=service_id))
-
-
-@main.route(
- "/services//service-settings/letter-contact//delete",
- methods=['POST'],
-)
-@user_has_permissions('manage_service')
-def service_delete_letter_contact(service_id, letter_contact_id):
- service_api_client.delete_letter_contact(
- service_id=current_service.id,
- letter_contact_id=letter_contact_id,
- )
- return redirect(url_for('.service_letter_contact_details', service_id=current_service.id))
-
-
@main.route("/services//service-settings/sms-sender", methods=['GET'])
@user_has_permissions('manage_service', 'manage_api_keys')
def service_sms_senders(service_id):
@@ -956,51 +818,6 @@ def service_preview_email_branding(service_id):
)
-@main.route("/services//service-settings/set-letter-branding", methods=['GET', 'POST'])
-@user_is_platform_admin
-def service_set_letter_branding(service_id):
- letter_branding = letter_branding_client.get_all_letter_branding()
-
- form = AdminSetLetterBrandingForm(
- all_branding_options=get_branding_as_value_and_label(letter_branding),
- current_branding=current_service.letter_branding_id,
- )
-
- if form.validate_on_submit():
- return redirect(url_for(
- '.service_preview_letter_branding',
- service_id=service_id,
- branding_style=form.branding_style.data,
- ))
-
- return render_template(
- 'views/service-settings/set-letter-branding.html',
- form=form,
- search_form=SearchByNameForm()
- )
-
-
-@main.route("/services//service-settings/preview-letter-branding", methods=['GET', 'POST'])
-@user_is_platform_admin
-def service_preview_letter_branding(service_id):
- branding_style = request.args.get('branding_style')
-
- form = AdminPreviewBrandingForm(branding_style=branding_style)
-
- if form.validate_on_submit():
- current_service.update(
- letter_branding=form.branding_style.data
- )
- return redirect(url_for('.service_settings', service_id=service_id))
-
- return render_template(
- 'views/service-settings/preview-letter-branding.html',
- form=form,
- service_id=service_id,
- action=url_for('main.service_preview_letter_branding', service_id=service_id),
- )
-
-
@main.route("/services//service-settings/link-service-to-organisation", methods=['GET', 'POST'])
@user_is_platform_admin
def link_service_to_organisation(service_id):
@@ -1156,46 +973,6 @@ def email_branding_something_else(service_id):
)
-@main.route("/services//service-settings/letter-branding", methods=['GET', 'POST'])
-@user_has_permissions('manage_service')
-def letter_branding_request(service_id):
- form = ChooseLetterBrandingForm(current_service)
- from_template = request.args.get('from_template')
- branding_name = current_service.letter_branding_name
- if form.validate_on_submit():
- ticket_message = render_template(
- 'support-tickets/branding-request.txt',
- current_branding=branding_name,
- branding_requested=dict(form.options.choices)[form.options.data],
- detail=form.something_else.data,
- )
- ticket = NotifySupportTicket(
- subject=f'Letter branding request - {current_service.name}',
- message=ticket_message,
- ticket_type=NotifySupportTicket.TYPE_QUESTION,
- user_name=current_user.name,
- user_email=current_user.email_address,
- org_id=current_service.organisation_id,
- org_type=current_service.organisation_type,
- service_id=current_service.id
- )
- zendesk_client.send_ticket_to_zendesk(ticket)
- flash((
- 'Thanks for your branding request. We’ll get back to you '
- 'within one working day.'
- ), 'default')
- return redirect(url_for(
- '.view_template', service_id=current_service.id, template_id=from_template
- ) if from_template else url_for('.service_settings', service_id=current_service.id))
-
- return render_template(
- 'views/service-settings/branding/letter-branding-options.html',
- form=form,
- branding_name=branding_name,
- from_template=from_template
- )
-
-
@main.route("/services//data-retention", methods=['GET'])
@user_is_platform_admin
def data_retention(service_id):
diff --git a/app/main/views/sub_navigation_dictionaries.py b/app/main/views/sub_navigation_dictionaries.py
index df5a21918..f1ad0b092 100644
--- a/app/main/views/sub_navigation_dictionaries.py
+++ b/app/main/views/sub_navigation_dictionaries.py
@@ -12,10 +12,6 @@ def features_nav():
"name": "Text messages",
"link": "main.features_sms",
},
- # {
- # "name": "Letters",
- # "link": "main.features_letters",
- # },
]
},
{
@@ -67,14 +63,6 @@ def using_notify_nav():
# "name": "Send files by email",
# "link": "main.send_files_by_email",
# },
- # {
- # "name": "Upload a letter",
- # "link": "main.upload_a_letter",
- # },
- # {
- # "name": "Letter specification",
- # "link": "main.letter_specification",
- # },
]
},
# {
diff --git a/app/main/views/templates.py b/app/main/views/templates.py
index d20756616..6ebafa539 100644
--- a/app/main/views/templates.py
+++ b/app/main/views/templates.py
@@ -11,8 +11,7 @@ from flask import (
)
from flask_login import current_user
from notifications_python_client.errors import HTTPError
-from notifications_utils import LETTER_MAX_PAGE_COUNT, SMS_CHAR_COUNT_LIMIT
-from notifications_utils.pdf import is_letter_too_long
+from notifications_utils import SMS_CHAR_COUNT_LIMIT
from app import (
current_service,
@@ -26,8 +25,6 @@ from app.formatters import character_count, message_count
from app.main import main, no_cookie
from app.main.forms import (
EmailTemplateForm,
- LetterTemplateForm,
- LetterTemplatePostageForm,
SearchTemplatesForm,
SetTemplateSenderForm,
SMSTemplateForm,
@@ -37,15 +34,14 @@ from app.main.forms import (
from app.main.views.send import get_sender_details
from app.models.service import Service
from app.models.template_list import TemplateList, TemplateLists
-from app.template_previews import TemplatePreview, get_page_count_for_letter
+from app.template_previews import TemplatePreview
from app.utils import NOTIFICATION_TYPES, should_skip_template_page
from app.utils.templates import get_template
-from app.utils.user import user_has_permissions, user_is_platform_admin
+from app.utils.user import user_has_permissions
form_objects = {
'email': EmailTemplateForm,
'sms': SMSTemplateForm,
- 'letter': LetterTemplateForm,
}
@@ -61,27 +57,14 @@ def view_template(service_id, template_id):
'.set_sender', service_id=service_id, template_id=template_id
))
- page_count = get_page_count_for_letter(template)
-
return render_template(
'views/templates/template.html',
template=get_template(
template,
current_service,
- letter_preview_url=url_for(
- 'no_cookie.view_letter_template_preview',
- service_id=service_id,
- template_id=template_id,
- filetype='png',
- ),
show_recipient=True,
- page_count=page_count,
),
- template_postage=template["postage"],
user_has_template_permission=user_has_template_permission,
- letter_too_long=is_letter_too_long(page_count),
- letter_max_pages=LETTER_MAX_PAGE_COUNT,
- page_count=page_count
)
@@ -199,7 +182,6 @@ def get_template_nav_label(value):
'all': 'All',
'sms': 'Text message',
'email': 'Email',
- 'letter': 'Letter',
}[value]
@@ -218,56 +200,10 @@ def get_template_nav_items(template_folder_id):
]
-@no_cookie.route("/services//templates/.")
-@user_has_permissions()
-def view_letter_template_preview(service_id, template_id, filetype):
- if filetype not in ('pdf', 'png'):
- abort(404)
-
- db_template = current_service.get_template(template_id)
-
- return TemplatePreview.from_database_object(db_template, filetype, page=request.args.get('page'))
-
-
-@no_cookie.route("/templates/letter-preview-image/")
-@user_is_platform_admin
-def letter_branding_preview_image(filename):
- template = {
- 'subject': 'An example letter',
- 'content': (
- 'Lorem Ipsum is simply dummy text of the printing and typesetting '
- 'industry.\n\nLorem Ipsum has been the industry’s standard dummy '
- 'text ever since the 1500s, when an unknown printer took a galley '
- 'of type and scrambled it to make a type specimen book.\n\n'
- '# History\n\nIt has survived not only\n\n'
- '* five centuries\n'
- '* but also the leap into electronic typesetting\n\n'
- 'It was popularised in the 1960s with the release of Letraset '
- 'sheets containing Lorem Ipsum passages, and more recently with '
- 'desktop publishing software like Aldus PageMaker including '
- 'versions of Lorem Ipsum.\n\n'
- 'The point of using Lorem Ipsum is that it has a more-or-less '
- 'normal distribution of letters, as opposed to using ‘Content '
- 'here, content here’, making it look like readable English.'
- ),
- 'template_type': 'letter',
- }
- filename = None if filename == 'no-branding' else filename
-
- return TemplatePreview.from_example_template(template, filename)
-
-
-def _view_template_version(service_id, template_id, version, letters_as_pdf=False):
+def _view_template_version(service_id, template_id, version):
return dict(template=get_template(
current_service.get_template(template_id, version=version),
- current_service,
- letter_preview_url=url_for(
- 'no_cookie.view_template_version_preview',
- service_id=service_id,
- template_id=template_id,
- version=version,
- filetype='png',
- ) if not letters_as_pdf else None
+ current_service
))
@@ -295,22 +231,6 @@ def _add_template_by_type(template_type, template_folder_id):
service_id=current_service.id,
))
- if template_type == 'letter':
- blank_letter = service_api_client.create_service_template(
- 'New letter template',
- 'letter',
- 'Body',
- current_service.id,
- 'Main heading',
- 'normal',
- template_folder_id
- )
- return redirect(url_for(
- '.view_template',
- service_id=current_service.id,
- template_id=blank_letter['data']['id'],
- ))
-
return redirect(url_for(
'.add_service_template',
service_id=current_service.id,
@@ -724,12 +644,6 @@ def delete_service_template(service_id, template_id):
template=get_template(
template,
current_service,
- letter_preview_url=url_for(
- 'no_cookie.view_letter_template_preview',
- service_id=service_id,
- template_id=template['id'],
- filetype='png',
- ),
show_recipient=True,
),
user_has_template_permission=True,
@@ -746,12 +660,6 @@ def confirm_redact_template(service_id, template_id):
template=get_template(
template,
current_service,
- letter_preview_url=url_for(
- 'no_cookie.view_letter_template_preview',
- service_id=service_id,
- template_id=template_id,
- filetype='png',
- ),
show_recipient=True,
),
user_has_template_permission=True,
@@ -786,13 +694,6 @@ def view_template_versions(service_id, template_id):
get_template(
template,
current_service,
- letter_preview_url=url_for(
- 'no_cookie.view_template_version_preview',
- service_id=service_id,
- template_id=template_id,
- version=template['version'],
- filetype='png',
- )
)
for template in service_api_client.get_service_template_versions(service_id, template_id)['data']
]
@@ -835,36 +736,11 @@ def set_template_sender(service_id, template_id):
)
-@main.route('/services//templates//edit-postage', methods=['GET', 'POST'])
-@user_has_permissions('manage_templates')
-def edit_template_postage(service_id, template_id):
- template = current_service.get_template_with_user_permission_or_403(template_id, current_user)
- if template["template_type"] != "letter":
- abort(404)
- form = LetterTemplatePostageForm(**template)
- if form.validate_on_submit():
- postage = form.postage.data
- service_api_client.update_service_template_postage(service_id, template_id, postage)
-
- return redirect(url_for('.view_template', service_id=service_id, template_id=template_id))
-
- return render_template(
- 'views/templates/edit-template-postage.html',
- form=form,
- service_id=service_id,
- template_id=template_id,
- template_postage=template["postage"]
- )
-
-
def get_template_sender_form_dict(service_id, template):
context = {
'email': {
'field_name': 'email_address'
},
- 'letter': {
- 'field_name': 'contact_block'
- },
'sms': {
'field_name': 'sms_sender'
}
@@ -881,5 +757,5 @@ def get_template_sender_form_dict(service_id, template):
context['value_and_label'] = [(sender['id'], nl2br(sender[sender_format])) for sender in service_senders]
context['value_and_label'].insert(0, ('', 'Blank')) # Add blank option to start of list
- context['current_choice'] = template['service_letter_contact'] if template['service_letter_contact'] else ''
+ context['current_choice'] = ''
return context
diff --git a/app/main/views/uploads.py b/app/main/views/uploads.py
index 910fe36e4..8ab61cc0c 100644
--- a/app/main/views/uploads.py
+++ b/app/main/views/uploads.py
@@ -1,50 +1,23 @@
-import base64
import itertools
-import json
-import uuid
from datetime import datetime
from io import BytesIO
from zipfile import BadZipFile
-from flask import (
- abort,
- current_app,
- flash,
- redirect,
- render_template,
- request,
- send_file,
- url_for,
-)
+from flask import flash, redirect, render_template, request, send_file, url_for
from notifications_utils.insensitive_dict import InsensitiveDict
-from notifications_utils.pdf import pdf_page_count
-from notifications_utils.postal_address import PostalAddress
from notifications_utils.recipients import RecipientCSV
from notifications_utils.sanitise_text import SanitiseASCII
-from PyPDF2.errors import PdfReadError
-from requests import RequestException
from xlrd.biffh import XLRDError
from xlrd.xldate import XLDateError
-from app import current_service, notification_api_client, service_api_client
-from app.extensions import antivirus_client
+from app import current_service
from app.main import main
-from app.main.forms import CsvUploadForm, LetterUploadPostageForm, PDFUploadForm
+from app.main.forms import CsvUploadForm
from app.models.contact_list import ContactList
-from app.s3_client.s3_letter_upload_client import (
- LetterNotFoundError,
- backup_original_letter_to_s3,
- get_letter_metadata,
- get_letter_pdf_and_metadata,
- get_transient_letter_file_location,
- upload_letter_to_s3,
-)
-from app.template_previews import TemplatePreview, sanitise_letter
from app.utils import unicode_truncate
from app.utils.csv import Spreadsheet, get_errors_for_csv
-from app.utils.letters import get_letter_validation_error
from app.utils.pagination import generate_next_dict, generate_previous_dict
-from app.utils.templates import get_sample_template, get_template
+from app.utils.templates import get_sample_template
from app.utils.user import user_has_permissions
MAX_FILE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB
@@ -82,253 +55,6 @@ def uploads(service_id):
)
-@main.route("/services//upload-letter", methods=['GET', 'POST'])
-@user_has_permissions('send_messages')
-def upload_letter(service_id):
- form = PDFUploadForm()
- error = {}
-
- if form.validate_on_submit():
- pdf_file_bytes = form.file.data.read()
- original_filename = form.file.data.filename
-
- if current_app.config['ANTIVIRUS_ENABLED']:
- virus_free = antivirus_client.scan(BytesIO(pdf_file_bytes))
- if not virus_free:
- return invalid_upload_error('Your file contains a virus')
-
- if len(pdf_file_bytes) > MAX_FILE_UPLOAD_SIZE:
- return invalid_upload_error('Your file is too big', 'Files must be smaller than 2MB.')
-
- try:
- # TODO: get page count from the sanitise response once template preview handles malformed files nicely
- page_count = pdf_page_count(BytesIO(pdf_file_bytes))
- except PdfReadError:
- current_app.logger.error('Invalid PDF uploaded for service_id: {}'.format(service_id))
- return invalid_upload_error(
- "There’s a problem with your file",
- 'Notify cannot read this PDF. Save a new copy of your file and try again.'
- )
-
- upload_id = uuid.uuid4()
- file_location = get_transient_letter_file_location(service_id, upload_id)
-
- try:
- response = sanitise_letter(
- BytesIO(pdf_file_bytes),
- upload_id=upload_id,
- allow_international_letters=current_service.has_permission(
- 'international_letters'
- ),
- )
- response.raise_for_status()
- except RequestException as ex:
- if ex.response is not None and ex.response.status_code == 400:
- validation_failed_message = response.json().get('message')
- invalid_pages = response.json().get('invalid_pages')
-
- status = 'invalid'
- upload_letter_to_s3(
- pdf_file_bytes,
- file_location=file_location,
- status=status,
- page_count=page_count,
- filename=original_filename,
- message=validation_failed_message,
- invalid_pages=invalid_pages)
- else:
- raise ex
- else:
- response = response.json()
- recipient = response['recipient_address']
- status = 'valid'
- file_contents = base64.b64decode(response['file'].encode())
-
- upload_letter_to_s3(
- file_contents,
- file_location=file_location,
- status=status,
- page_count=page_count,
- filename=original_filename,
- recipient=recipient)
-
- backup_original_letter_to_s3(
- pdf_file_bytes,
- upload_id=upload_id,
- )
-
- return redirect(
- url_for(
- 'main.uploaded_letter_preview',
- service_id=current_service.id,
- file_id=upload_id,
- )
- )
-
- if form.file.errors:
- error = _get_error_from_upload_form(form.file.errors[0])
-
- return render_template(
- 'views/uploads/choose-file.html',
- error=error,
- form=form
- )
-
-
-def invalid_upload_error(error_title, error_detail=None):
- return render_template(
- 'views/uploads/choose-file.html',
- error={'title': error_title, 'detail': error_detail},
- form=PDFUploadForm()
- ), 400
-
-
-def _get_error_from_upload_form(form_errors):
- error = {}
- if 'PDF' in form_errors:
- error['title'] = 'Wrong file type'
- error['detail'] = form_errors
- else: # No file was uploaded error
- error['title'] = form_errors
-
- return error
-
-
-@main.route("/services//preview-letter/")
-@user_has_permissions('send_messages')
-def uploaded_letter_preview(service_id, file_id):
- re_upload_form = PDFUploadForm()
-
- try:
- metadata = get_letter_metadata(service_id, file_id)
- except LetterNotFoundError as e:
- current_app.logger.warning(e)
-
- # If the file is missing it's likely because this is a duplicate
- # request, the notification already exists and the file has been
- # moved to a different bucket. Note that the ID of a precompiled
- # notification is always set to the file_id.
- return redirect(url_for(
- '.view_notification',
- service_id=service_id,
- notification_id=file_id,
- ))
-
- original_filename = metadata.get('filename')
- page_count = metadata.get('page_count')
- status = metadata.get('status')
- error_shortcode = metadata.get('message')
- invalid_pages = metadata.get('invalid_pages')
- postal_address = PostalAddress(metadata.get('recipient', ''))
-
- if invalid_pages:
- invalid_pages = json.loads(invalid_pages)
-
- error_message = get_letter_validation_error(error_shortcode, invalid_pages, page_count)
- template_dict = service_api_client.get_precompiled_template(service_id)
- # Override pre compiled letter template postage to none as it has not yet been picked even though
- # the pre compiled letter template has its postage set as second class as the DB currently requires
- # a non null value of postage for letter templates
- template_dict['postage'] = None
-
- form = LetterUploadPostageForm(
- postage_zone=postal_address.postage
- )
-
- template = get_template(
- template_dict,
- service_id,
- letter_preview_url=url_for(
- '.view_letter_upload_as_preview',
- service_id=service_id,
- file_id=file_id
- ),
- page_count=page_count
- )
-
- return render_template(
- 'views/uploads/preview.html',
- original_filename=original_filename,
- template=template,
- status=status,
- file_id=file_id,
- message=error_message,
- error_code=error_shortcode,
- form=form,
- allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS,
- postal_address=postal_address,
- re_upload_form=re_upload_form
- )
-
-
-@main.route("/services//preview-letter-image/")
-@user_has_permissions('send_messages')
-def view_letter_upload_as_preview(service_id, file_id):
- try:
- page = int(request.args.get('page'))
- except ValueError:
- abort(400)
-
- pdf_file, metadata = get_letter_pdf_and_metadata(service_id, file_id)
- invalid_pages = json.loads(metadata.get('invalid_pages', '[]'))
-
- if (
- metadata.get('message') == 'content-outside-printable-area' and
- page in invalid_pages
- ):
- return TemplatePreview.from_invalid_pdf_file(pdf_file, page)
- else:
- return TemplatePreview.from_valid_pdf_file(pdf_file, page)
-
-
-@main.route("/services//upload-letter/send/", methods=['POST'])
-@user_has_permissions('send_messages', restrict_admin_usage=True)
-def send_uploaded_letter(service_id, file_id):
- if not current_service.has_permission('letter'):
- abort(403)
-
- try:
- metadata = get_letter_metadata(service_id, file_id)
- except LetterNotFoundError as e:
- current_app.logger.warning(e)
-
- # If the file is missing it's likely because this is a duplicate
- # request, the notification already exists and the file has been
- # moved to a different bucket. Note that the ID of a precompiled
- # notification is always set to the file_id.
- return redirect(url_for(
- '.view_notification',
- service_id=service_id,
- notification_id=file_id,
- ))
-
- if metadata.get('status') != 'valid':
- abort(403)
-
- postal_address = PostalAddress(metadata.get('recipient'))
-
- form = LetterUploadPostageForm(
- postage_zone=postal_address.postage
- )
-
- if not form.validate_on_submit():
- return uploaded_letter_preview(service_id, file_id)
-
- notification_api_client.send_precompiled_letter(
- service_id,
- metadata.get('filename'),
- file_id,
- form.postage.data,
- postal_address.raw_address,
- )
-
- return redirect(url_for(
- '.view_notification',
- service_id=service_id,
- notification_id=file_id,
- ))
-
-
@main.route("/services//upload-contact-list", methods=['GET', 'POST'])
@user_has_permissions('send_messages')
def upload_contact_list(service_id):
diff --git a/app/models/event.py b/app/models/event.py
index 0a778fe24..2697ec01b 100644
--- a/app/models/event.py
+++ b/app/models/event.py
@@ -79,14 +79,6 @@ class ServiceEvent(Event):
def format_inbound_api(self):
return 'Updated the callback for received text messages'
- def format_letter_branding(self):
- if self.value_to is None:
- return 'Removed the logo from this service’s letters'
- return 'Updated the logo on this service’s letters'
-
- def format_letter_contact_block(self):
- return 'Updated the default letter contact block for this service'
-
def format_message_limit(self):
return (
'{} this service’s daily message limit from {} to {}'
diff --git a/app/models/job.py b/app/models/job.py
index bf527c376..237452965 100644
--- a/app/models/job.py
+++ b/app/models/job.py
@@ -1,7 +1,3 @@
-from notifications_utils.letter_timings import (
- CANCELLABLE_JOB_LETTER_STATUSES,
- get_letter_timings,
-)
from werkzeug.utils import cached_property
from app.models import JSONModel, ModelList, PaginatedModelList
@@ -51,10 +47,6 @@ class Job(JSONModel):
def upload_type(self):
return self._dict.get('upload_type')
- @property
- def pdf_letter(self):
- return self.upload_type == 'letter'
-
@property
def processing_started(self):
if not self._dict.get('processing_started'):
@@ -139,24 +131,11 @@ class Job(JSONModel):
@property
def uncancellable_notifications(self):
+ # TODO: this is redundant now
return (
n for n in self.all_notifications
- if n['status'] not in CANCELLABLE_JOB_LETTER_STATUSES
)
- @cached_property
- def postage(self):
- # There might be no notifications if the job has only just been
- # created and the tasks haven't run yet
- try:
- return self.all_notifications[0]['postage']
- except IndexError:
- return self.template['postage']
-
- @property
- def letter_timings(self):
- return get_letter_timings(self.created_at, postage=self.postage)
-
@property
def failure_rate(self):
if not self.notifications_delivered:
diff --git a/app/models/organisation.py b/app/models/organisation.py
index ac846bbbb..0492c6897 100644
--- a/app/models/organisation.py
+++ b/app/models/organisation.py
@@ -10,7 +10,6 @@ from app.models import (
SortByNameMixin,
)
from app.notify_client.email_branding_client import email_branding_client
-from app.notify_client.letter_branding_client import letter_branding_client
from app.notify_client.organisations_api_client import organisations_client
@@ -32,7 +31,6 @@ class Organisation(JSONModel, SortByNameMixin):
'active',
'crown',
'organisation_type',
- 'letter_branding_id',
'email_branding_id',
'agreement_signed',
'agreement_signed_at',
@@ -167,13 +165,6 @@ class Organisation(JSONModel, SortByNameMixin):
return self.email_branding['name']
return 'GOV.UK'
- @cached_property
- def letter_branding(self):
- if self.letter_branding_id:
- return letter_branding_client.get_letter_branding(
- self.letter_branding_id
- )
-
@cached_property
def agreement_signed_by(self):
if self.agreement_signed_by_id:
diff --git a/app/models/service.py b/app/models/service.py
index 054a5107c..3ea3d75aa 100644
--- a/app/models/service.py
+++ b/app/models/service.py
@@ -18,7 +18,6 @@ from app.notify_client.email_branding_client import email_branding_client
from app.notify_client.inbound_number_client import inbound_number_client
from app.notify_client.invite_api_client import invite_api_client
from app.notify_client.job_api_client import job_api_client
-from app.notify_client.letter_branding_client import letter_branding_client
from app.notify_client.organisations_api_client import organisations_client
from app.notify_client.service_api_client import service_api_client
from app.notify_client.template_folder_api_client import (
@@ -52,20 +51,17 @@ class Service(JSONModel, SortByNameMixin):
'service_callback_api',
'volume_email',
'volume_sms',
- 'volume_letter',
}
TEMPLATE_TYPES = (
'email',
'sms',
- 'letter',
)
ALL_PERMISSIONS = TEMPLATE_TYPES + (
'edit_folder_permissions',
'email_auth',
'inbound_sms',
- 'international_letters',
'international_sms',
'upload_document',
)
@@ -260,7 +256,6 @@ class Service(JSONModel, SortByNameMixin):
self.consent_to_research is not None and any((
self.volume_email,
self.volume_sms,
- self.volume_letter,
))
)
@@ -364,55 +359,11 @@ class Service(JSONModel, SortByNameMixin):
self.sms_sender_is_govuk,
))
- @cached_property
- def letter_contact_details(self):
- return service_api_client.get_letter_contacts(self.id)
-
- @property
- def count_letter_contact_details(self):
- return len(self.letter_contact_details)
-
- @property
- def default_letter_contact_block(self):
- return next(
- (
- letter_contact_block
- for letter_contact_block in self.letter_contact_details
- if letter_contact_block['is_default']
- ), None
- )
-
- @property
- def default_letter_contact_block_html(self):
- # import in the function to prevent cyclical imports
- from app import nl2br
-
- if self.default_letter_contact_block:
- return nl2br(self.default_letter_contact_block['contact_block'])
- return ''
-
- def edit_letter_contact_block(self, id, contact_block, is_default):
- service_api_client.update_letter_contact(
- self.id, letter_contact_id=id, contact_block=contact_block, is_default=is_default,
- )
-
- def remove_default_letter_contact_block(self):
- if self.default_letter_contact_block:
- self.edit_letter_contact_block(
- self.default_letter_contact_block['id'],
- self.default_letter_contact_block['contact_block'],
- is_default=False,
- )
-
- def get_letter_contact_block(self, id):
- return service_api_client.get_letter_contact(self.id, id)
-
@property
def volumes(self):
return sum(filter(None, (
self.volume_email,
self.volume_sms,
- self.volume_letter,
)))
@property
@@ -465,26 +416,10 @@ class Service(JSONModel, SortByNameMixin):
return 'GOV.UK'
return self.email_branding['name']
- @cached_property
- def letter_branding_name(self):
- if self.letter_branding is None:
- return 'no'
- return self.letter_branding['name']
-
@property
def needs_to_change_email_branding(self):
return self.email_branding_id is None and self.organisation_type != Organisation.TYPE_CENTRAL
- @property
- def letter_branding_id(self):
- return self._dict['letter_branding']
-
- @cached_property
- def letter_branding(self):
- if self.letter_branding_id:
- return letter_branding_client.get_letter_branding(self.letter_branding_id)
- return None
-
@cached_property
def organisation(self):
return Organisation.from_id(self.organisation_id)
@@ -592,26 +527,6 @@ class Service(JSONModel, SortByNameMixin):
}
)
- @cached_property
- def returned_letter_statistics(self):
- return service_api_client.get_returned_letter_statistics(self.id)
-
- @cached_property
- def returned_letter_summary(self):
- return service_api_client.get_returned_letter_summary(self.id)
-
- @property
- def count_of_returned_letters_in_last_7_days(self):
- return self.returned_letter_statistics['returned_letter_count']
-
- @property
- def date_of_most_recent_returned_letter_report(self):
- return self.returned_letter_statistics['most_recent_report']
-
- @property
- def has_returned_letters(self):
- return bool(self.date_of_most_recent_returned_letter_report)
-
@property
def contact_lists(self):
return ContactLists(self.id)
diff --git a/app/navigation.py b/app/navigation.py
index 7a0468508..812386686 100644
--- a/app/navigation.py
+++ b/app/navigation.py
@@ -47,7 +47,6 @@ class HeaderNavigation(Navigation):
'features': {
'features',
'features_email',
- 'features_letters',
'features_sms',
'message_status',
'roadmap',
@@ -84,12 +83,10 @@ class HeaderNavigation(Navigation):
'change_user_auth',
'clear_cache',
'create_email_branding',
- 'create_letter_branding',
'edit_sms_provider_ratio',
'email_branding',
'find_services_by_name',
'find_users_by_email',
- 'letter_branding',
'live_services',
'live_services_csv',
'notifications_sent_by_service',
@@ -101,12 +98,10 @@ class HeaderNavigation(Navigation):
'platform_admin',
'platform_admin_list_complaints',
'platform_admin_reports',
- 'platform_admin_returned_letters',
'platform_admin_splash_page',
'suspend_service',
'trial_services',
'update_email_branding',
- 'update_letter_branding',
'user_information',
'view_provider',
'view_providers',
@@ -136,8 +131,6 @@ class MainNavigation(Navigation):
'conversation',
'inbox',
'monthly',
- 'returned_letter_summary',
- 'returned_letters',
'service_dashboard',
'template_usage',
'view_notification',
@@ -156,11 +149,9 @@ class MainNavigation(Navigation):
'copy_template',
'delete_service_template',
'edit_service_template',
- 'edit_template_postage',
'manage_template_folder',
'send_messages',
'send_one_off',
- 'send_one_off_letter_address',
'send_one_off_step',
'send_one_off_to_myself',
'no_cookie.send_test_preview',
@@ -176,8 +167,6 @@ class MainNavigation(Navigation):
'save_contact_list',
'contact_list',
'delete_contact_list',
- 'upload_letter',
- 'uploaded_letter_preview',
'uploads',
'view_job',
'view_jobs',
@@ -205,36 +194,27 @@ class MainNavigation(Navigation):
'email_branding_request',
'email_branding_something_else',
'estimate_usage',
- 'letter_branding_request',
'link_service_to_organisation',
'request_to_go_live',
'service_add_email_reply_to',
- 'service_add_letter_contact',
'service_add_sms_sender',
'service_agreement',
'service_accept_agreement',
'service_confirm_agreement',
'service_confirm_delete_email_reply_to',
- 'service_confirm_delete_letter_contact',
'service_confirm_delete_sms_sender',
'service_edit_email_reply_to',
- 'service_edit_letter_contact',
'service_edit_sms_sender',
'service_email_reply_to',
- 'service_letter_contact_details',
- 'service_make_blank_default_letter_contact',
'service_name_change',
'service_preview_email_branding',
- 'service_preview_letter_branding',
'service_set_auth_type',
'service_set_channel',
'send_files_by_email_contact_details',
'service_set_email_branding',
'service_set_inbound_number',
'service_set_inbound_sms',
- 'service_set_international_letters',
'service_set_international_sms',
- 'service_set_letters',
'service_set_reply_to_email',
'service_set_sms_prefix',
'service_verify_reply_to_address',
@@ -244,7 +224,6 @@ class MainNavigation(Navigation):
'set_free_sms_allowance',
'set_message_limit',
'set_rate_limit',
- 'service_set_letter_branding',
'submit_request_to_go_live',
},
'api-integration': {
@@ -269,7 +248,6 @@ class CaseworkNavigation(Navigation):
'choose_from_contact_list',
'choose_template',
'send_one_off',
- 'send_one_off_letter_address',
'send_one_off_step',
'send_one_off_to_myself',
},
@@ -285,8 +263,6 @@ class CaseworkNavigation(Navigation):
'save_contact_list',
'contact_list',
'delete_contact_list',
- 'upload_letter',
- 'uploaded_letter_preview',
'uploads',
},
}
@@ -304,14 +280,12 @@ class OrgNavigation(Navigation):
'edit_organisation_crown_status',
'edit_organisation_domains',
'edit_organisation_email_branding',
- 'edit_organisation_letter_branding',
'edit_organisation_domains',
'edit_organisation_go_live_notes',
'edit_organisation_name',
'edit_organisation_notes',
'edit_organisation_type',
'organisation_preview_email_branding',
- 'organisation_preview_letter_branding',
'organisation_settings',
},
diff --git a/app/notify_client/letter_branding_client.py b/app/notify_client/letter_branding_client.py
deleted file mode 100644
index 736d9d025..000000000
--- a/app/notify_client/letter_branding_client.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from app.notify_client import NotifyAdminAPIClient, cache
-
-
-class LetterBrandingClient(NotifyAdminAPIClient):
-
- @cache.set('letter_branding-{branding_id}')
- def get_letter_branding(self, branding_id):
- return self.get(url='/letter-branding/{}'.format(branding_id))
-
- @cache.set('letter_branding')
- def get_all_letter_branding(self):
- return self.get(url='/letter-branding')
-
- @cache.delete('letter_branding')
- def create_letter_branding(self, filename, name):
- data = {
- "filename": filename,
- "name": name,
- }
- return self.post(url="/letter-branding", data=data)
-
- @cache.delete('letter_branding')
- @cache.delete('letter_branding-{branding_id}')
- def update_letter_branding(self, branding_id, filename, name):
- data = {
- "filename": filename,
- "name": name,
- }
- return self.post(url="/letter-branding/{}".format(branding_id), data=data)
-
-
-letter_branding_client = LetterBrandingClient()
diff --git a/app/notify_client/letter_jobs_client.py b/app/notify_client/letter_jobs_client.py
deleted file mode 100644
index 6314642ee..000000000
--- a/app/notify_client/letter_jobs_client.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from app.notify_client import NotifyAdminAPIClient
-
-
-class LetterJobsClient(NotifyAdminAPIClient):
-
- def submit_returned_letters(self, references):
- return self.post(
- url='/letters/returned',
- data={'references': references}
- )
-
-
-letter_jobs_client = LetterJobsClient()
diff --git a/app/notify_client/notification_api_client.py b/app/notify_client/notification_api_client.py
index 5e38a8835..36afbbf2e 100644
--- a/app/notify_client/notification_api_client.py
+++ b/app/notify_client/notification_api_client.py
@@ -63,16 +63,6 @@ class NotificationApiClient(NotifyAdminAPIClient):
data = _attach_current_user(data)
return self.post(url='/service/{}/send-notification'.format(service_id), data=data)
- def send_precompiled_letter(self, service_id, filename, file_id, postage, recipient_address):
- data = {
- 'filename': filename,
- 'file_id': file_id,
- 'postage': postage,
- 'recipient_address': recipient_address
- }
- data = _attach_current_user(data)
- return self.post(url='/service/{}/send-pdf-letter'.format(service_id), data=data)
-
def get_notification(self, service_id, notification_id):
return self.get(url='/service/{}/notifications/{}'.format(service_id, notification_id))
@@ -84,29 +74,7 @@ class NotificationApiClient(NotifyAdminAPIClient):
include_one_off=False,
count_pages=False
)
- return self.map_letters_to_accepted(ret)
-
- @staticmethod
- def map_letters_to_accepted(notifications):
- for notification in notifications['notifications']:
- if notification['notification_type'] == 'letter':
- if notification['status'] in ('created', 'sending'):
- notification['status'] = 'accepted'
-
- if notification['status'] in ('delivered', 'returned-letter'):
- notification['status'] = 'received'
- return notifications
-
- def get_notification_letter_preview(self, service_id, notification_id, file_type, page=None):
-
- get_url = '/service/{}/template/preview/{}/{}{}'.format(
- service_id,
- notification_id,
- file_type,
- '?page={}'.format(page) if page else ''
- )
-
- return self.get(url=get_url)
+ return ret
def update_notification_to_cancelled(self, service_id, notification_id):
return self.post(
diff --git a/app/notify_client/service_api_client.py b/app/notify_client/service_api_client.py
index 83e8d3f87..449a5e4b0 100644
--- a/app/notify_client/service_api_client.py
+++ b/app/notify_client/service_api_client.py
@@ -88,8 +88,6 @@ class ServiceAPIClient(NotifyAdminAPIClient):
'free_sms_fragment_limit',
'go_live_at',
'go_live_user',
- 'letter_branding',
- 'letter_contact_block',
'message_limit',
'name',
'notes',
@@ -103,7 +101,6 @@ class ServiceAPIClient(NotifyAdminAPIClient):
'restricted',
'sms_sender',
'volume_email',
- 'volume_letter',
'volume_sms',
}
if disallowed_attributes:
@@ -236,14 +233,6 @@ class ServiceAPIClient(NotifyAdminAPIClient):
data
)
- @cache.delete('service-{service_id}-templates')
- @cache.delete_by_pattern('service-{service_id}-template-*')
- def update_service_template_postage(self, service_id, template_id, postage):
- return self.post(
- "/service/{0}/template/{1}".format(service_id, template_id),
- _attach_current_user({'postage': postage})
- )
-
@cache.set('service-{service_id}-template-{template_id}-version-{version}')
def get_service_template(self, service_id, template_id, version=None):
"""
@@ -444,45 +433,6 @@ class ServiceAPIClient(NotifyAdminAPIClient):
data=None
)
- def get_letter_contacts(self, service_id):
- return self.get("/service/{}/letter-contact".format(service_id))
-
- def get_letter_contact(self, service_id, letter_contact_id):
- return self.get("/service/{}/letter-contact/{}".format(service_id, letter_contact_id))
-
- @cache.delete('service-{service_id}')
- @cache.delete_by_pattern('service-{service_id}-template-*')
- def add_letter_contact(self, service_id, contact_block, is_default=False):
- return self.post(
- "/service/{}/letter-contact".format(service_id),
- data={
- "contact_block": contact_block,
- "is_default": is_default
- }
- )
-
- @cache.delete('service-{service_id}')
- @cache.delete_by_pattern('service-{service_id}-template-*')
- def update_letter_contact(self, service_id, letter_contact_id, contact_block, is_default=False):
- return self.post(
- "/service/{}/letter-contact/{}".format(
- service_id,
- letter_contact_id,
- ),
- data={
- "contact_block": contact_block,
- "is_default": is_default
- }
- )
-
- @cache.delete('service-{service_id}')
- @cache.delete_by_pattern('service-{service_id}-template-*')
- def delete_letter_contact(self, service_id, letter_contact_id):
- return self.post(
- "/service/{}/letter-contact/{}/archive".format(service_id, letter_contact_id),
- data=None
- )
-
def get_sms_senders(self, service_id):
return self.get(
"/service/{}/sms-sender".format(service_id)
@@ -575,17 +525,6 @@ class ServiceAPIClient(NotifyAdminAPIClient):
def get_service_data_retention(self, service_id):
return self.get("/service/{}/data-retention".format(service_id))
- @cache.set('service-{service_id}-returned-letters-statistics')
- def get_returned_letter_statistics(self, service_id):
- return self.get("service/{}/returned-letter-statistics".format(service_id))
-
- @cache.set('service-{service_id}-returned-letters-summary')
- def get_returned_letter_summary(self, service_id):
- return self.get("service/{}/returned-letter-summary".format(service_id))
-
- def get_returned_letters(self, service_id, reported_at):
- return self.get("service/{}/returned-letters?reported_at={}".format(service_id, reported_at))
-
def get_notification_count(self, service_id):
# if cache is not set, or not enabled, return 0
diff --git a/app/s3_client/s3_letter_upload_client.py b/app/s3_client/s3_letter_upload_client.py
deleted file mode 100644
index 4bb479fdb..000000000
--- a/app/s3_client/s3_letter_upload_client.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import json
-import urllib
-
-import botocore
-from boto3 import resource
-from flask import current_app
-from notifications_utils.s3 import s3upload as utils_s3upload
-
-
-class LetterNotFoundError(Exception):
- pass
-
-
-def get_transient_letter_file_location(service_id, upload_id):
- return 'service-{}/{}.pdf'.format(service_id, upload_id)
-
-
-def backup_original_letter_to_s3(
- data,
- upload_id,
-):
- utils_s3upload(
- filedata=data,
- region=current_app.config['AWS_REGION'],
- bucket_name=current_app.config['PRECOMPILED_ORIGINALS_BACKUP_LETTERS'],
- file_location=f'{upload_id}.pdf',
- )
-
-
-def upload_letter_to_s3(
- data,
- *,
- file_location,
- status,
- page_count,
- filename,
- message=None,
- invalid_pages=None,
- recipient=None
-):
- # Use of urllib.parse.quote encodes metadata into ascii, which is required by s3.
- # Making sure data for displaying to users is decoded is taken care of by LetterMetadata
- metadata = {
- 'status': status,
- 'page_count': str(page_count),
- 'filename': urllib.parse.quote(filename),
- }
- if message:
- metadata['message'] = message
- if invalid_pages:
- metadata['invalid_pages'] = json.dumps(invalid_pages)
- if recipient:
- metadata['recipient'] = urllib.parse.quote(recipient)
-
- utils_s3upload(
- filedata=data,
- region=current_app.config['AWS_REGION'],
- bucket_name=current_app.config['TRANSIENT_UPLOADED_LETTERS'],
- file_location=file_location,
- metadata=metadata,
- )
-
-
-class LetterMetadata:
- KEYS_TO_DECODE = ["filename", "recipient"]
-
- def __init__(self, metadata):
- self._metadata = metadata
-
- def get(self, key, default=None):
- value = self._metadata.get(key, default)
- if value and key in self.KEYS_TO_DECODE:
- value = urllib.parse.unquote(value)
- return value
-
-
-def get_letter_s3_object(service_id, file_id):
- try:
- file_location = get_transient_letter_file_location(service_id, file_id)
- s3 = resource('s3')
- return s3.Object(current_app.config['TRANSIENT_UPLOADED_LETTERS'], file_location).get()
- except botocore.exceptions.ClientError as e:
- if e.response['Error']['Code'] == 'NoSuchKey':
- raise LetterNotFoundError(f'Letter not found for service {service_id} and file {file_id}')
-
- raise
-
-
-def get_letter_pdf_and_metadata(service_id, file_id):
- s3_object = get_letter_s3_object(service_id, file_id)
- pdf = s3_object['Body'].read()
- return pdf, LetterMetadata(s3_object['Metadata'])
-
-
-def get_letter_metadata(service_id, file_id):
- s3_object = get_letter_s3_object(service_id, file_id)
- return LetterMetadata(s3_object['Metadata'])
diff --git a/app/s3_client/s3_logo_client.py b/app/s3_client/s3_logo_client.py
index aba076624..dc432abec 100644
--- a/app/s3_client/s3_logo_client.py
+++ b/app/s3_client/s3_logo_client.py
@@ -8,9 +8,6 @@ from app.s3_client import get_s3_object
TEMP_TAG = 'temp-{user_id}_'
EMAIL_LOGO_LOCATION_STRUCTURE = '{temp}{unique_id}-{filename}'
-LETTER_PREFIX = 'letters/static/images/letter-template/'
-LETTER_TEMP_TAG = LETTER_PREFIX + TEMP_TAG
-LETTER_TEMP_LOGO_LOCATION = 'letters/static/images/letter-template/temp-{user_id}_{unique_id}-{filename}'
def get_logo_location(filename=None):
@@ -53,10 +50,6 @@ def get_temp_truncated_filename(filename, user_id):
return filename[len(TEMP_TAG.format(user_id=user_id)):]
-def get_letter_filename_with_no_path_or_extension(filename):
- return filename[len(LETTER_PREFIX):-4]
-
-
def upload_email_logo(filename, filedata, user_id):
upload_file_name = EMAIL_LOGO_LOCATION_STRUCTURE.format(
temp=TEMP_TAG.format(user_id=user_id),
@@ -77,26 +70,6 @@ def upload_email_logo(filename, filedata, user_id):
return upload_file_name
-def upload_letter_temp_logo(filename, filedata, user_id):
- upload_filename = LETTER_TEMP_LOGO_LOCATION.format(
- user_id=user_id,
- unique_id=str(uuid.uuid4()),
- filename=filename
- )
- bucket_name = bucket_creds('bucket')
- utils_s3upload(
- filedata=filedata,
- region=bucket_creds('region'),
- bucket_name=bucket_name,
- file_location=upload_filename,
- content_type='image/svg+xml',
- access_key=bucket_creds('access_key_id'),
- secret_key=bucket_creds('secret_access_key'),
- )
-
- return upload_filename
-
-
def permanent_email_logo_name(filename, user_id):
if filename.startswith(TEMP_TAG.format(user_id=user_id)):
return get_temp_truncated_filename(filename=filename, user_id=user_id)
@@ -104,38 +77,13 @@ def permanent_email_logo_name(filename, user_id):
return filename
-def permanent_letter_logo_name(filename, extension):
- return LETTER_PREFIX + filename + '.' + extension
-
-
-def letter_filename_for_db(filename, user_id):
- filename = get_letter_filename_with_no_path_or_extension(filename)
-
- if filename.startswith(TEMP_TAG.format(user_id=user_id)):
- filename = get_temp_truncated_filename(filename=filename, user_id=user_id)
-
- return filename
-
-
def delete_email_temp_files_created_by(user_id):
for obj in get_s3_objects_filter_by_prefix(TEMP_TAG.format(user_id=user_id)):
delete_s3_object(obj.key)
-def delete_letter_temp_files_created_by(user_id):
- for obj in get_s3_objects_filter_by_prefix(LETTER_TEMP_TAG.format(user_id=user_id)):
- delete_s3_object(obj.key)
-
-
def delete_email_temp_file(filename):
if not filename.startswith(TEMP_TAG[:5]):
raise ValueError('Not a temp file: {}'.format(filename))
delete_s3_object(filename)
-
-
-def delete_letter_temp_file(filename):
- if not filename.startswith(LETTER_TEMP_TAG[:43]):
- raise ValueError('Not a temp file: {}'.format(filename))
-
- delete_s3_object(filename)
diff --git a/app/templates/components/branding-preview.html b/app/templates/components/branding-preview.html
index 67d59ed79..db3e3001d 100644
--- a/app/templates/components/branding-preview.html
+++ b/app/templates/components/branding-preview.html
@@ -5,7 +5,3 @@
{% macro email_branding_preview(branding_style) %}
{{ branding_preview(branding_style, 'main.email_template') }}
{% endmacro %}
-
-{% macro letter_branding_preview(branding_style) %}
- {{ branding_preview(branding_style, 'main.letter_template') }}
-{% endmacro %}
diff --git a/app/templates/components/table.html b/app/templates/components/table.html
index cfd029ee8..f5e15c9eb 100644
--- a/app/templates/components/table.html
+++ b/app/templates/components/table.html
@@ -147,7 +147,7 @@
{% macro notification_status_field(notification) %}
- {% set displayed_on_single_line = notification.status in ['created', 'pending', 'pending-virus-check', 'sending', 'delivered', 'returned-letter', 'accepted', 'received'] %}
+ {% set displayed_on_single_line = notification.status in ['created', 'pending', 'pending-virus-check', 'sending', 'delivered', 'accepted', 'received'] %}
{% if not notification %}
{% call field(align='right') %}{% endcall %}
@@ -166,18 +166,10 @@
{% endif %}
- {% if notification['notification_type'] == "letter" %}
- {% if notification.status == 'permanent-failure' %}
- {{ (notification.updated_at)|format_datetime_short }}
- {% else %}
- {{ (notification.created_at)|format_datetime_short }}
- {% endif %}
- {% else %}
- {{ notification.status|format_notification_status_as_time(
- notification.created_at|format_datetime_short,
- (notification.updated_at or notification.created_at)|format_datetime_short
- ) }}
- {% endif %}
+ {{ notification.status|format_notification_status_as_time(
+ notification.created_at|format_datetime_short,
+ (notification.updated_at or notification.created_at)|format_datetime_short
+ ) }}
{% if displayed_on_single_line %}{% endif %}
{% endcall %}
diff --git a/app/templates/partials/check/letter-too-long.html b/app/templates/partials/check/letter-too-long.html
deleted file mode 100644
index 50d377945..000000000
--- a/app/templates/partials/check/letter-too-long.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
- Your letter is too long
-
-
- Letters must be {{ letter_max_pages }} pages or less ({{ letter_max_pages // 2 }} double-sided sheets of paper).
-
- Your letter is {{ page_count }} pages long.
-
diff --git a/app/templates/partials/check/letter-validation-failed-banner.html b/app/templates/partials/check/letter-validation-failed-banner.html
deleted file mode 100644
index 6596c31dd..000000000
--- a/app/templates/partials/check/letter-validation-failed-banner.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% from "components/banner.html" import banner_wrapper %}
-
-{% call banner_wrapper(type='dangerous') %}
- {% if message is string %}
-
diff --git a/app/templates/support-tickets/go-live-request.txt b/app/templates/support-tickets/go-live-request.txt
index 37abc2933..5042bf653 100644
--- a/app/templates/support-tickets/go-live-request.txt
+++ b/app/templates/support-tickets/go-live-request.txt
@@ -22,7 +22,6 @@ Agreement signed on behalf of: {{ organisation.agreement_signed_on_behalf_of_ema
Emails in next year: {{ service.volume_email|format_thousands }}
Text messages in next year: {{ service.volume_sms|format_thousands }}
-Letters in next year: {{ service.volume_letter|format_thousands }}
Consent to research: {{ service.consent_to_research|format_yes_no }}
Other live services for that user: {{ user.live_services|format_yes_no }}
diff --git a/app/templates/views/check/column-errors.html b/app/templates/views/check/column-errors.html
index d8084654d..838b6bc7e 100644
--- a/app/templates/views/check/column-errors.html
+++ b/app/templates/views/check/column-errors.html
@@ -52,16 +52,9 @@
There’s a problem with your column names
- {% if template.template_type == 'letter' %}
-
- Your file needs at least 3 address columns, for example ‘address line 1’,
- ‘address line 2’ and ‘address line 3’.
-
- {% else %}
Your file needs a column called ‘{{ first_recipient_column }}’.
- {% endif %}
Right now it has {{ recipients.column_headers | formatted_list(
prefix='one column, called ',
@@ -113,16 +106,6 @@
{% include "partials/check/not-allowed-to-send-to.html" %}
{% endwith %}
- {% elif trying_to_send_letters_in_trial_mode %}
-
-
- {% with
- count_of_recipients=count_of_recipients
- %}
- {% include "partials/check/trying-to-send-letters-in-trial-mode.html" %}
- {% endwith %}
-
diff --git a/app/templates/views/check/row-errors.html b/app/templates/views/check/row-errors.html
index 2f28ab991..84f548af6 100644
--- a/app/templates/views/check/row-errors.html
+++ b/app/templates/views/check/row-errors.html
@@ -74,20 +74,6 @@
No content for this message
{% elif item.message_too_long %}
Message is too long
- {% elif not item.as_postal_address.has_enough_lines %}
- Address must be at least {{ letter_min_address_lines }} lines long
- {% elif item.as_postal_address.has_too_many_lines %}
- Address must be no more than {{ letter_max_address_lines }} lines long
- {% elif not item.as_postal_address.has_valid_last_line %}
- {% if item.as_postal_address.allow_international_letters %}
- Last line of the address must be a UK postcode or another country
- {% elif item.as_postal_address.international %}
- You do not have permission to send letters to other countries
- {% else %}
- Last line of the address must be a real UK postcode
- {% endif %}
- {% elif item.as_postal_address.has_invalid_characters %}
- Address lines must not start with any of the following characters: @ ( ) = [ ] ” \ / , < > ~
{% endif %}
{% endcall %}
diff --git a/app/templates/views/dashboard/_inbox.html b/app/templates/views/dashboard/_inbox.html
index 9dd6dc9a8..90f9fb6b6 100644
--- a/app/templates/views/dashboard/_inbox.html
+++ b/app/templates/views/dashboard/_inbox.html
@@ -14,17 +14,4 @@
{% endif %}
{% endif %}
- {% if current_service.has_returned_letters %}
-
-
- {{ current_service.count_of_returned_letters_in_last_7_days|format_thousands }}
-
-
- returned {{ current_service.count_of_returned_letters_in_last_7_days|message_count_label('letter', suffix='') }}
-
-
- latest report {{ current_service.date_of_most_recent_returned_letter_report|format_delta_days }}
-
-
- {% endif %}
diff --git a/app/templates/views/dashboard/_jobs.html b/app/templates/views/dashboard/_jobs.html
index 37cc923f9..0c50a57e1 100644
--- a/app/templates/views/dashboard/_jobs.html
+++ b/app/templates/views/dashboard/_jobs.html
@@ -7,7 +7,7 @@
caption="Recent files uploaded",
caption_visible=False,
empty_message=(
- 'Upload a letter and Notify will print, pack and post it for you.' if current_service.has_permission('letter') else 'You have not uploaded any files yet.'
+ 'You have not uploaded any files yet.'
),
field_headings=[
'File',
@@ -62,29 +62,12 @@
suffix='waiting to send'
)
) }}
- {% elif item.template_type == 'letter' %}
- {{ big_number(
- item.notification_count,
- smallest=True,
- label=item.notification_count|message_count_label(
- item.template_type,
- suffix=''
- )
- ) }}
{% elif item.upload_type == 'contact_list' %}
{{ big_number(
item.row_count,
smallest=True,
label="saved {}".format(item.row_count|recipient_count_label(item.template_type))
) }}
- {% elif item.pdf_letter %}
-
- {% for line in item.recipient.splitlines() %}
- {% if loop.index < 3 %}
- {{ line }}
- {% endif %}
- {% endfor %}
-
Notify can send UK letters by first or second class post.
-
First class letters are delivered one day after they’re dispatched. Second class letters are delivered 2 days after they’re dispatched.
-
Letters are printed at 5:30pm and dispatched the next working day (Monday to Friday). Royal Mail delivers from Monday to Saturday, excluding bank holidays.
-
-
Branding
-
Add your organisation’s logo to your letter templates.
Go to the {{ service_link(current_service, 'main.choose_template', 'templates') }} page and choose an existing template.
Select Send.
-
If you’re sending emails, select Upload a list of email addresses. If you’re sending text messages, select Upload a list of phone numbers. If you’re sending letters, select Upload a list of addresses.
+
If you’re sending emails, select Upload a list of email addresses. If you’re sending text messages, select Upload a list of phone numbers.
Letter templates can include headings and bullets.
-
Notify does not allow bold, italics, subheadings, underlined text or different fonts. This is because they can make it harder for users to read what you’ve written.
diff --git a/app/templates/views/platform-admin/daily-volumes-report.html b/app/templates/views/platform-admin/daily-volumes-report.html
index 1fc4e939f..420dbcc5e 100644
--- a/app/templates/views/platform-admin/daily-volumes-report.html
+++ b/app/templates/views/platform-admin/daily-volumes-report.html
@@ -34,8 +34,6 @@
('sms fragments', 'The number of text message fragments sent times the rate multiplier'),
('sms chargeable units', 'The number of text message fragments sent'),
('email totals', 'The number of emails sent'),
- ('letter totals', 'The number of letters sent'),
- ('letter sheet totals', 'The number of sheets sent')
] %}
{% call row() %}
{{ text_field(column_heading) }}
diff --git a/app/templates/views/platform-admin/get-billing-report.html b/app/templates/views/platform-admin/get-billing-report.html
index 8979c2d4d..d9517ee9e 100644
--- a/app/templates/views/platform-admin/get-billing-report.html
+++ b/app/templates/views/platform-admin/get-billing-report.html
@@ -31,8 +31,6 @@
{% for column_heading, description in [
('sms cost', 'The total cost of text messages sent after a service has used its free allowance.'),
('sms chargeable units', 'The number of fragments sent after a service has used its free allowance. This number takes into account the cost multiplier for sending international text messages.' | safe),
- ('letter cost', 'The total cost of letters sent by a service.'),
- ('letter breakdown', 'The number of letters sent by a service, grouped by postage and unit cost.'),
('purchase order number, contact names, contact email addresses and billing reference', 'We add this data manually based on the information we get from services. You can help by adding it to the service settings page.'),
] %}
{% call row() %}
diff --git a/app/templates/views/platform-admin/returned-letters.html b/app/templates/views/platform-admin/returned-letters.html
deleted file mode 100644
index d4a55ce15..000000000
--- a/app/templates/views/platform-admin/returned-letters.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "views/platform-admin/_base_template.html" %}
-{% from "components/textbox.html" import textbox %}
-{% from "components/page-footer.html" import sticky_page_footer %}
-{% from "components/form.html" import form_wrapper %}
-
-{% block per_page_title %}
- {{ page_title|capitalize }}
-{% endblock %}
-
-{% block platform_admin_content %}
-
-
Submit returned letters
- {% call form_wrapper() %}
- {{ textbox(form.references, width='1-1', rows=8, autosize=True) }}
- {{ sticky_page_footer("Submit") }}
- {% endcall %}
-
-{% endblock %}
diff --git a/app/templates/views/platform-admin/services.html b/app/templates/views/platform-admin/services.html
index 0f7a2a975..ff731a0a8 100644
--- a/app/templates/views/platform-admin/services.html
+++ b/app/templates/views/platform-admin/services.html
@@ -13,7 +13,6 @@
field_headings=[
right_aligned_field_heading('Emails'),
right_aligned_field_heading('Text messages'),
- right_aligned_field_heading('Letters')
],
field_headings_visible=False,
) %}
@@ -33,7 +32,7 @@
{% endcall %}
{% call row() %}
- {% for channel in ('email', 'sms', 'letter') %}
+ {% for channel in ('email', 'sms') %}
{% call field(border=False) %}
{{ big_number(
service['stats'][channel]['requested'],
diff --git a/app/templates/views/platform-admin/volumes-by-service-report.html b/app/templates/views/platform-admin/volumes-by-service-report.html
index 1ad2de2bf..54d15c931 100644
--- a/app/templates/views/platform-admin/volumes-by-service-report.html
+++ b/app/templates/views/platform-admin/volumes-by-service-report.html
@@ -33,9 +33,6 @@
('sms notifications', 'The number of text messages sent by the service.'),
('sms chargeable units', 'The number of text message fragments times the rate multiplier sent by the service.'),
('email totals', 'The number of emails sent by a service'),
- ('letter totals', 'The number of letters sent by a service'),
- ('letter costs', 'The cost of the letters, rate * letter totals'),
- ('letter sheet totals', 'The number of sheet sent by a service')
] %}
{% call row() %}
{{ text_field(column_heading) }}
diff --git a/app/templates/views/pricing/how-to-pay.html b/app/templates/views/pricing/how-to-pay.html
index 9598ba50e..760d644cf 100644
--- a/app/templates/views/pricing/how-to-pay.html
+++ b/app/templates/views/pricing/how-to-pay.html
@@ -10,7 +10,7 @@
{{ page_header('How to pay') }}
You’ll only pay for the additional text messages that you send. There’s no monthly charge, no setup fee and no procurement cost.
-
-{% endblock %}
diff --git a/app/templates/views/service-settings/set-sms.html b/app/templates/views/service-settings/set-sms.html
index 2432baebd..f7ed24860 100644
--- a/app/templates/views/service-settings/set-sms.html
+++ b/app/templates/views/service-settings/set-sms.html
@@ -27,7 +27,7 @@
after your free allowance.
- See pricing for more details.
+ See pricing for more details.
{% call form_wrapper() %}
{{ form.enabled }}
diff --git a/app/templates/views/signedout.html b/app/templates/views/signedout.html
index 32e2aa81b..0a33ab5b4 100644
--- a/app/templates/views/signedout.html
+++ b/app/templates/views/signedout.html
@@ -58,8 +58,7 @@
Control your content
- You do not need any technical knowledge to create email,
- text message or letter templates.
+ You do not need any technical knowledge to create message templates.
@@ -152,11 +151,6 @@
free text messages a year,
then {{ sms_rate }} pence per message
-
-
Letters
-
41 pence
- to print and post a one page letter
-
There’s no monthly charge, no setup fee and no procurement process.
diff --git a/app/templates/views/templates/_template.html b/app/templates/views/templates/_template.html
index 70aa2c640..f9dcafead 100644
--- a/app/templates/views/templates/_template.html
+++ b/app/templates/views/templates/_template.html
@@ -1,18 +1,6 @@
{% from "components/banner.html" import banner_wrapper %}
- {% if current_user.has_permissions('manage_templates') and template.template_type == 'letter' %}
- {% if not current_service.letter_branding_id %}
- Add logo
- {% endif %}
- Change postage
- Edit letter template
- {% if current_service.count_letter_contact_details %}
- Edit letter contact block
- {% else %}
- Edit letter contact block
- {% endif %}
- {% endif %}
{{ template|string }}
@@ -30,34 +18,19 @@
{% else %}
- {% if template.template_type == 'letter' %}
- {% if letter_too_long %}
- {% call banner_wrapper(type='dangerous') %}
- {% include "partials/check/letter-too-long.html" %}
- {% endcall %}
- {% endif %}
- {% if current_user.has_permissions('send_messages', restrict_admin_usage=True) and not letter_too_long %}
-