diff --git a/.github/actions/deploy-proxy/action.yml b/.github/actions/deploy-proxy/action.yml index ff9d1cbe8..13bdc494f 100644 --- a/.github/actions/deploy-proxy/action.yml +++ b/.github/actions/deploy-proxy/action.yml @@ -36,7 +36,7 @@ runs: - name: Copy config files shell: bash run: cp ./deploy-config/egress_proxy/${{ inputs.app }}.*.acl ${{ steps.create-temp-dir.outputs.path }} - - name: Build and deploy proxy + - name: Deploy proxy shell: bash working-directory: ${{ steps.create-temp-dir.outputs.path }} - run: make && ./bin/cf-deployproxy -a ${{ inputs.app }} -p egress-proxy -e egress_proxy + run: ./bin/cf-deployproxy -a ${{ inputs.app }} -p egress-proxy -e egress_proxy diff --git a/app/__init__.py b/app/__init__.py index 062f5b892..d2232343a 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -94,7 +94,6 @@ from app.notify_client import InviteTokenError from app.notify_client.api_key_api_client import api_key_api_client from app.notify_client.billing_api_client import billing_api_client from app.notify_client.complaint_api_client import complaint_api_client -from app.notify_client.contact_list_api_client import contact_list_api_client from app.notify_client.email_branding_client import email_branding_client from app.notify_client.events_api_client import events_api_client from app.notify_client.inbound_number_client import inbound_number_client @@ -155,35 +154,27 @@ def _csp(config): "'self'", asset_domain ], + "frame-ancestors": "'none'", + "form-action": "'self'", "script-src": [ "'self'", - "'unsafe-eval'", asset_domain, - "*.google-analytics.com", + "'unsafe-eval'", "https://js-agent.newrelic.com", - "https://*.nr-data.net", - "data:" + "https://gov-bam.nr-data.net", ], "connect-src": [ "'self'", - "*.google-analytics.com", - "https://*.nr-data.net" + "https://gov-bam.nr-data.net" ], "style-src": [ "'self'", asset_domain ], - "font-src": [ - "'self'", - asset_domain, - "data:" - ], "img-src": [ "'self'", asset_domain, - logo_domain, - "*.google-analytics.com", - "data:" + logo_domain ] } @@ -216,7 +207,6 @@ def create_app(application): # API clients api_key_api_client, billing_api_client, - contact_list_api_client, complaint_api_client, email_branding_client, events_api_client, @@ -247,6 +237,22 @@ def create_app(application): application, content_security_policy=_csp(application.config), content_security_policy_nonce_in=['style-src', 'script-src'], + permissions_policy={ + 'accelerometer': '()', + 'ambient-light-sensor': '()', + 'autoplay': '()', + 'battery': '()', + 'camera': '()', + 'document-domain': '()', + 'geolocation': '()', + 'gyroscope': '()', + 'local-fonts': '()', + 'magnetometer': '()', + 'microphone': '()', + 'midi': '()', + 'payment': '()', + 'screen-wake-lock': '()' + }, frame_options='deny', force_https=(application.config['HTTP_PROTOCOL'] == 'https') ) diff --git a/app/assets/error_pages/5xx.html b/app/assets/error_pages/5xx.html index 4787a9393..13adac296 100644 --- a/app/assets/error_pages/5xx.html +++ b/app/assets/error_pages/5xx.html @@ -41,14 +41,14 @@ - + class="govuk-header__logotype-fallback-image" width="36" height="32"> US @@ -108,11 +108,6 @@ Government Licence v3.0, except where otherwise stated - diff --git a/app/config.py b/app/config.py index f873d5b8f..2779364df 100644 --- a/app/config.py +++ b/app/config.py @@ -94,7 +94,6 @@ class Development(Config): # Buckets CSV_UPLOAD_BUCKET = _s3_credentials_from_env('CSV') - CONTACT_LIST_BUCKET = _s3_credentials_from_env('CONTACT') LOGO_UPLOAD_BUCKET = _s3_credentials_from_env('LOGO') # credential overrides @@ -127,8 +126,6 @@ class Production(Config): # buckets CSV_UPLOAD_BUCKET = cloud_config.s3_credentials( f"notify-api-csv-upload-bucket-{getenv('NOTIFY_ENVIRONMENT')}") - CONTACT_LIST_BUCKET = cloud_config.s3_credentials( - f"notify-api-contact-list-bucket-{getenv('NOTIFY_ENVIRONMENT')}") LOGO_UPLOAD_BUCKET = cloud_config.s3_credentials( f"notify-admin-logo-upload-bucket-{getenv('NOTIFY_ENVIRONMENT')}") diff --git a/app/main/forms.py b/app/main/forms.py index cb2d72f45..fed38965b 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -1145,18 +1145,6 @@ class OrganisationOrganisationTypeForm(StripWhitespaceForm): organisation_type = OrganisationTypeField('What type of organization is this?') -class OrganisationCrownStatusForm(StripWhitespaceForm): - crown_status = GovukRadiosField( - 'Is this organization a crown body?', - choices=[ - ('crown', 'Yes'), - ('non-crown', 'No'), - ('unknown', 'Not sure'), - ], - thing='whether this organization is a crown body', - ) - - class OrganisationAgreementSignedForm(StripWhitespaceForm): agreement_signed = GovukRadiosField( 'Has this organization signed the agreement?', @@ -1209,13 +1197,10 @@ class CreateServiceForm(StripWhitespaceForm): class AdminNewOrganisationForm( RenameOrganisationForm, - OrganisationOrganisationTypeForm, - OrganisationCrownStatusForm, + OrganisationOrganisationTypeForm ): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - # Don’t offer the ‘not sure’ choice - self.crown_status.choices = self.crown_status.choices[:-1] class AdminServiceSMSAllowanceForm(StripWhitespaceForm): diff --git a/app/main/views/agreement.py b/app/main/views/agreement.py index 930185924..13c3a93a5 100644 --- a/app/main/views/agreement.py +++ b/app/main/views/agreement.py @@ -23,8 +23,6 @@ def service_agreement(service_id): return redirect( url_for('main.add_organisation_from_nhs_local_service', service_id=current_service.id) ) - if current_service.organisation.crown is None: - return render_template('views/agreement/service-agreement-choose.html') if current_service.organisation.agreement_signed: return render_template('views/agreement/service-agreement-signed.html') return render_template('views/agreement/service-agreement.html') @@ -33,9 +31,7 @@ def service_agreement(service_id): @main.route('/services//agreement.pdf') @user_has_permissions('manage_service') def service_download_agreement(service_id): - return send_file(**get_mou( - current_service.organisation.crown_status_or_404 - )) + return send_file(**get_mou()) @main.route('/services//agreement/accept', methods=['GET', 'POST']) @@ -85,14 +81,13 @@ def service_confirm_agreement(service_id): @main.route('/agreement/', endpoint='public_agreement') @main.route('/agreement/.pdf', endpoint='public_download_agreement') def public_agreement(variant): - - if variant not in {'crown', 'non-crown'}: + # originally we returned 404 if variant was not in ['crown', 'not_crown']. Will we be using agreement.pdf? + # for now this is just to keep tests working as expected. + if variant != "agreement": abort(404) if request.endpoint == 'main.public_download_agreement': - return send_file(**get_mou( - organisation_is_crown=(variant == 'crown') - )) + return send_file(**get_mou()) return render_template( 'views/agreement/agreement-public.html', diff --git a/app/main/views/organisations.py b/app/main/views/organisations.py index 82d98fc05..3c059fb22 100644 --- a/app/main/views/organisations.py +++ b/app/main/views/organisations.py @@ -23,7 +23,6 @@ from app.main.forms import ( AdminSetEmailBrandingForm, InviteOrgUserForm, OrganisationAgreementSignedForm, - OrganisationCrownStatusForm, OrganisationOrganisationTypeForm, RenameOrganisationForm, SearchByNameForm, @@ -278,35 +277,6 @@ def edit_organisation_type(org_id): ) -@main.route("/organisations//settings/edit-crown-status", methods=['GET', 'POST']) -@user_is_platform_admin -def edit_organisation_crown_status(org_id): - - form = OrganisationCrownStatusForm( - crown_status={ - True: 'crown', - False: 'non-crown', - None: 'unknown', - }.get(current_organisation.crown) - ) - - if form.validate_on_submit(): - organisations_client.update_organisation( - current_organisation.id, - crown={ - 'crown': True, - 'non-crown': False, - 'unknown': None, - }.get(form.crown_status.data), - ) - return redirect(url_for('.organisation_settings', org_id=org_id)) - - return render_template( - 'views/organisations/organisation/settings/edit-crown-status.html', - form=form, - ) - - @main.route("/organisations//settings/edit-agreement", methods=['GET', 'POST']) @user_is_platform_admin def edit_organisation_agreement(org_id): @@ -499,6 +469,4 @@ def organisation_billing(org_id): @main.route('/organisations//agreement.pdf') @user_is_platform_admin def organisation_download_agreement(org_id): - return send_file(**get_mou( - current_organisation.crown_status_or_404 - )) + return send_file(**get_mou()) diff --git a/app/main/views/send.py b/app/main/views/send.py index cc37f5427..764b3daee 100644 --- a/app/main/views/send.py +++ b/app/main/views/send.py @@ -35,7 +35,6 @@ from app.main.forms import ( SetSenderForm, get_placeholder_form_instance, ) -from app.models.contact_list import ContactList, ContactListsAlphabetical from app.models.user import Users from app.s3_client.s3_csv_client import ( get_csv_metadata, @@ -390,47 +389,6 @@ def send_one_off_step(service_id, template_id, step_index): ) -@main.route( - '/services//send/' - '/from-contact-list' -) -@user_has_permissions('send_messages') -def choose_from_contact_list(service_id, template_id): - db_template = current_service.get_template_with_user_permission_or_403( - template_id, current_user - ) - template = get_template( - db_template, current_service, - ) - return render_template( - 'views/send-contact-list.html', - contact_lists=ContactListsAlphabetical( - current_service.id, - template_type=template.template_type, - ), - template=template, - ) - - -@main.route( - '/services//send/' - '/from-contact-list/' -) -@user_has_permissions('send_messages') -def send_from_contact_list(service_id, template_id, contact_list_id): - contact_list = ContactList.from_id( - contact_list_id, - service_id=current_service.id, - ) - return redirect(url_for( - 'main.check_messages', - service_id=current_service.id, - template_id=template_id, - upload_id=contact_list.copy_to_uploads(), - contact_list_id=contact_list.id, - )) - - 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 @@ -573,7 +531,6 @@ def start_job(service_id, upload_id): upload_id, service_id, scheduled_for=request.form.get('scheduled_for', ''), - contact_list_id=request.form.get('contact_list_id', ''), ) session.pop('sender_id', None) diff --git a/app/main/views/uploads.py b/app/main/views/uploads.py index 7cbe73410..0fcc6184d 100644 --- a/app/main/views/uploads.py +++ b/app/main/views/uploads.py @@ -1,23 +1,10 @@ -import itertools from datetime import datetime -from io import BytesIO -from zipfile import BadZipFile -from flask import flash, redirect, render_template, request, send_file, url_for -from notifications_utils.insensitive_dict import InsensitiveDict -from notifications_utils.recipients import RecipientCSV -from notifications_utils.sanitise_text import SanitiseASCII -from xlrd.biffh import XLRDError -from xlrd.xldate import XLDateError +from flask import render_template, request from app import current_service from app.main import main -from app.main.forms import CsvUploadForm -from app.models.contact_list import ContactList -from app.utils import unicode_truncate -from app.utils.csv import Spreadsheet, get_errors_for_csv from app.utils.pagination import generate_next_dict, generate_previous_dict -from app.utils.templates import get_sample_template from app.utils.user import user_has_permissions MAX_FILE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB @@ -39,7 +26,6 @@ def uploads(service_id): if uploads.current_page == 1: listed_uploads = ( - current_service.contact_lists + current_service.scheduled_jobs + uploads ) @@ -53,198 +39,3 @@ def uploads(service_id): next_page=next_page, now=datetime.utcnow().isoformat(), ) - - -@main.route("/services//upload-contact-list", methods=['GET', 'POST']) -@user_has_permissions('send_messages') -def upload_contact_list(service_id): - form = CsvUploadForm() - - if form.validate_on_submit(): - try: - upload_id = ContactList.upload( - current_service.id, - Spreadsheet.from_file_form(form).as_dict, - ) - file_name_metadata = unicode_truncate( - SanitiseASCII.encode(form.file.data.filename), - 1600 - ) - ContactList.set_metadata( - current_service.id, - upload_id, - original_file_name=file_name_metadata - ) - return redirect(url_for( - '.check_contact_list', - service_id=service_id, - upload_id=upload_id, - )) - except (UnicodeDecodeError, BadZipFile, XLRDError): - flash('Could not read {}. Try using a different file format.'.format( - form.file.data.filename - )) - except (XLDateError): - flash(( - '{} contains numbers or dates that Notify cannot understand. ' - 'Try formatting all columns as ‘text’ or export your file as CSV.' - ).format( - form.file.data.filename - )) - elif form.errors: - # just show the first error, as we don't expect the form to have more - # than one, since it only has one field - first_field_errors = list(form.errors.values())[0] - flash(first_field_errors[0]) - - return render_template( - 'views/uploads/contact-list/upload.html', - form=form, - allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS, - ) - - -@main.route( - "/services//check-contact-list/", - methods=['GET', 'POST'], -) -@user_has_permissions('send_messages') -def check_contact_list(service_id, upload_id): - - form = CsvUploadForm() - - contents = ContactList.download(service_id, upload_id) - first_row = contents.splitlines()[0].strip().rstrip(',') if contents else '' - original_file_name = ContactList.get_metadata(service_id, upload_id).get('original_file_name', '') - - template_type = InsensitiveDict({ - 'email address': 'email', - 'phone number': 'sms', - }).get(first_row) - - recipients = RecipientCSV( - contents, - template=get_sample_template(template_type or 'sms'), - guestlist=itertools.chain.from_iterable( - [user.name, user.mobile_number, user.email_address] - for user in current_service.active_users - ) if current_service.trial_mode else None, - allow_international_sms=current_service.has_permission('international_sms'), - max_initial_rows_shown=50, - max_errors_shown=50, - ) - - non_empty_column_headers = list(filter(None, recipients.column_headers)) - - if len(non_empty_column_headers) > 1 or not template_type or not recipients: - return render_template( - 'views/uploads/contact-list/too-many-columns.html', - recipients=recipients, - original_file_name=original_file_name, - template_type=template_type, - form=form, - allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS - ) - - if recipients.too_many_rows or not len(recipients): - return render_template( - 'views/uploads/contact-list/column-errors.html', - recipients=recipients, - original_file_name=original_file_name, - form=form, - allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS - ) - - row_errors = get_errors_for_csv(recipients, template_type) - if row_errors: - return render_template( - 'views/uploads/contact-list/row-errors.html', - recipients=recipients, - original_file_name=original_file_name, - row_errors=row_errors, - form=form, - allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS - ) - - if recipients.has_errors: - return render_template( - 'views/uploads/contact-list/column-errors.html', - recipients=recipients, - original_file_name=original_file_name, - form=form, - allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS - ) - - metadata_kwargs = { - 'row_count': len(recipients), - 'valid': True, - 'original_file_name': original_file_name, - 'template_type': template_type - } - - ContactList.set_metadata(service_id, upload_id, **metadata_kwargs) - - return render_template( - 'views/uploads/contact-list/ok.html', - recipients=recipients, - original_file_name=original_file_name, - upload_id=upload_id, - ) - - -@main.route("/services//save-contact-list/", methods=['POST']) -@user_has_permissions('send_messages') -def save_contact_list(service_id, upload_id): - ContactList.create(current_service.id, upload_id) - return redirect(url_for( - '.uploads', - service_id=current_service.id, - )) - - -@main.route("/services//contact-list/", methods=['GET']) -@user_has_permissions('send_messages') -def contact_list(service_id, contact_list_id): - contact_list = ContactList.from_id(contact_list_id, service_id=service_id) - return render_template( - 'views/uploads/contact-list/contact-list.html', - contact_list=contact_list, - jobs=contact_list.get_jobs( - page=1, - limit_days=current_service.get_days_of_retention(contact_list.template_type), - ), - ) - - -@main.route("/services//contact-list//delete", methods=['GET', 'POST']) -@user_has_permissions('manage_templates') -def delete_contact_list(service_id, contact_list_id): - contact_list = ContactList.from_id(contact_list_id, service_id=service_id) - - if request.method == 'POST': - contact_list.delete() - return redirect(url_for( - '.uploads', - service_id=service_id, - )) - - flash([ - f"Are you sure you want to delete ‘{contact_list.original_file_name}’?", - ], 'delete') - - return render_template( - 'views/uploads/contact-list/contact-list.html', - contact_list=contact_list, - confirm_delete_banner=True, - ) - - -@main.route("/services//contact-list/.csv", methods=['GET']) -@user_has_permissions('send_messages') -def download_contact_list(service_id, contact_list_id): - contact_list = ContactList.from_id(contact_list_id, service_id=service_id) - return send_file( - path_or_file=BytesIO(contact_list.contents.encode('utf-8')), - download_name=contact_list.saved_file_name, - as_attachment=True, - ) diff --git a/app/models/contact_list.py b/app/models/contact_list.py deleted file mode 100644 index dfecaf50a..000000000 --- a/app/models/contact_list.py +++ /dev/null @@ -1,195 +0,0 @@ -from functools import partial -from os import path -from uuid import uuid4 - -from flask import abort, current_app -from notifications_utils.formatters import strip_all_whitespace -from notifications_utils.recipients import RecipientCSV -from notifications_utils.s3 import s3upload as utils_s3upload -from werkzeug.utils import cached_property - -from app.models import JSONModel, ModelList -from app.models.job import PaginatedJobsAndScheduledJobs -from app.notify_client.contact_list_api_client import contact_list_api_client -from app.s3_client import ( - get_s3_contents, - get_s3_metadata, - get_s3_object, - set_s3_metadata, -) -from app.s3_client.s3_csv_client import s3upload, set_metadata_on_csv_upload -from app.utils.templates import get_sample_template - - -class ContactList(JSONModel): - - ALLOWED_PROPERTIES = { - 'id', - 'created_at', - 'created_by', - 'has_jobs', - 'recent_job_count', - 'service_id', - 'original_file_name', - 'row_count', - 'template_type', - } - - upload_type = 'contact_list' - - @classmethod - def from_id(cls, contact_list_id, *, service_id): - return cls(contact_list_api_client.get_contact_list( - service_id=service_id, - contact_list_id=contact_list_id, - )) - - @staticmethod - def get_bucket_credentials(key): - return current_app.config['CONTACT_LIST_BUCKET'][key] - - @staticmethod - def get_bucket_name(): - return ContactList.get_bucket_credentials('bucket') - - @staticmethod - def get_access_key(): - return ContactList.get_bucket_credentials('access_key_id') - - @staticmethod - def get_secret_key(): - return ContactList.get_bucket_credentials('secret_access_key') - - @staticmethod - def get_region(): - return ContactList.get_bucket_credentials('region') - - @staticmethod - def get_filename(service_id, upload_id): - return f"service-{service_id}-notify/{upload_id}.csv" - - @staticmethod - def get_s3_arguments(service_id, upload_id): - return ( - ContactList.get_bucket_name(), - ContactList.get_filename(service_id, upload_id), - ContactList.get_access_key(), - ContactList.get_secret_key(), - ContactList.get_region(), - ) - - @staticmethod - def upload(service_id, file_dict): - upload_id = str(uuid4()) - utils_s3upload( - filedata=file_dict['data'], - region=ContactList.get_region(), - bucket_name=ContactList.get_bucket_name(), - file_location=ContactList.get_filename(service_id, upload_id), - access_key=ContactList.get_access_key(), - secret_key=ContactList.get_secret_key(), - ) - return upload_id - - @staticmethod - def download(service_id, upload_id): - return strip_all_whitespace( - get_s3_contents( - get_s3_object(*ContactList.get_s3_arguments(service_id, upload_id)))) - - @staticmethod - def set_metadata(service_id, upload_id, **kwargs): - return set_s3_metadata(get_s3_object(*ContactList.get_s3_arguments(service_id, upload_id)), **kwargs) - - @staticmethod - def get_metadata(service_id, upload_id): - return get_s3_metadata(get_s3_object(*ContactList.get_s3_arguments(service_id, upload_id))) - - def copy_to_uploads(self): - raise RuntimeError("RCA probably an issue with copying between buckets") - metadata = self.get_metadata(self.service_id, self.id) - new_upload_id = s3upload( - self.service_id, - {'data': self.contents}, - ContactList.get_region(), - ) - set_metadata_on_csv_upload( - self.service_id, - new_upload_id, - **metadata, - ) - return new_upload_id - - @classmethod - def create(cls, service_id, upload_id): - - metadata = cls.get_metadata(service_id, upload_id) - - if not metadata.get('valid'): - abort(403) - - return cls(contact_list_api_client.create_contact_list( - service_id=service_id, - upload_id=upload_id, - original_file_name=metadata['original_file_name'], - row_count=int(metadata['row_count']), - template_type=metadata['template_type'], - )) - - def delete(self): - contact_list_api_client.delete_contact_list( - service_id=self.service_id, - contact_list_id=self.id, - ) - - @property - def contents(self): - return self.download(self.service_id, self.id) - - @cached_property - def recipients(self): - return RecipientCSV( - self.contents, - template=get_sample_template(self.template_type), - allow_international_sms=True, - max_initial_rows_shown=50, - ) - - @property - def saved_file_name(self): - file_name, extention = path.splitext(self.original_file_name) - return f'{file_name}.csv' - - def get_jobs(self, *, page, limit_days=None): - return PaginatedJobsAndScheduledJobs( - self.service_id, - contact_list_id=self.id, - page=page, - limit_days=limit_days, - ) - - -class ContactLists(ModelList): - - client_method = contact_list_api_client.get_contact_lists - model = ContactList - sort_function = partial( - sorted, - key=lambda item: item['created_at'], - reverse=True, - ) - - def __init__(self, service_id, template_type=None): - super().__init__(service_id) - self.items = self.sort_function([ - item for item in self.items - if template_type in {item['template_type'], None} - ]) - - -class ContactListsAlphabetical(ContactLists): - - sort_function = partial( - sorted, - key=lambda item: item['original_file_name'].lower(), - ) diff --git a/app/models/job.py b/app/models/job.py index 237452965..3fa1f7d33 100644 --- a/app/models/job.py +++ b/app/models/job.py @@ -172,10 +172,9 @@ class PaginatedJobs(PaginatedModelList, ImmediateJobs): client_method = job_api_client.get_page_of_jobs statuses = None - def __init__(self, service_id, *, contact_list_id=None, page=None, limit_days=None): + def __init__(self, service_id, *, page=None, limit_days=None): super().__init__( service_id, - contact_list_id=contact_list_id, statuses=self.statuses, page=page, limit_days=limit_days, diff --git a/app/models/organisation.py b/app/models/organisation.py index 0492c6897..282c1a851 100644 --- a/app/models/organisation.py +++ b/app/models/organisation.py @@ -1,6 +1,5 @@ from collections import OrderedDict -from flask import abort from werkzeug.utils import cached_property from app.models import ( @@ -29,7 +28,6 @@ class Organisation(JSONModel, SortByNameMixin): 'id', 'name', 'active', - 'crown', 'organisation_type', 'email_branding_id', 'agreement_signed', @@ -66,19 +64,13 @@ class Organisation(JSONModel, SortByNameMixin): def create_from_form(cls, form): return cls.create( name=form.name.data, - crown={ - 'crown': True, - 'non-crown': False, - 'unknown': None, - }.get(form.crown_status.data), organisation_type=form.organisation_type.data, ) @classmethod - def create(cls, name, crown, organisation_type, agreement_signed=False): + def create(cls, name, organisation_type, agreement_signed=False): return cls(organisations_client.create_organisation( name=name, - crown=crown, organisation_type=organisation_type, agreement_signed=agreement_signed, )) @@ -89,7 +81,6 @@ class Organisation(JSONModel, SortByNameMixin): if self._dict == {}: self.name = None - self.crown = None self.agreement_signed = None self.domains = [] self.organisation_type = None @@ -100,12 +91,6 @@ class Organisation(JSONModel, SortByNameMixin): def organisation_type_label(self): return self.TYPE_LABELS.get(self.organisation_type) - @property - def crown_status_or_404(self): - if self.crown is None: - abort(404) - return self.crown - @property def billing_details(self): billing_details = [ diff --git a/app/models/service.py b/app/models/service.py index 3ea3d75aa..b16faae37 100644 --- a/app/models/service.py +++ b/app/models/service.py @@ -3,7 +3,6 @@ from notifications_utils.serialised_model import SerialisedModelCollection from werkzeug.utils import cached_property from app.models import JSONModel, SortByNameMixin -from app.models.contact_list import ContactLists from app.models.job import ( ImmediateJobs, PaginatedJobs, @@ -527,10 +526,6 @@ class Service(JSONModel, SortByNameMixin): } ) - @property - def contact_lists(self): - return ContactLists(self.id) - class Services(SerialisedModelCollection): model = Service diff --git a/app/navigation.py b/app/navigation.py index 65c97deb3..ef29424f4 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -141,7 +141,6 @@ class MainNavigation(Navigation): 'add_service_template', 'check_messages', 'check_notification', - 'choose_from_contact_list', 'choose_template', 'choose_template_to_copy', 'confirm_redact_template', @@ -161,11 +160,6 @@ class MainNavigation(Navigation): 'view_template_versions', }, 'uploads': { - 'upload_contact_list', - 'check_contact_list', - 'save_contact_list', - 'contact_list', - 'delete_contact_list', 'uploads', 'view_job', 'view_jobs', @@ -244,7 +238,6 @@ class CaseworkNavigation(Navigation): mapping = { 'send-one-off': { - 'choose_from_contact_list', 'choose_template', 'send_one_off', 'send_one_off_step', @@ -257,11 +250,6 @@ class CaseworkNavigation(Navigation): 'uploads': { 'view_jobs', 'view_job', - 'upload_contact_list', - 'check_contact_list', - 'save_contact_list', - 'contact_list', - 'delete_contact_list', 'uploads', }, } @@ -276,7 +264,6 @@ class OrgNavigation(Navigation): 'settings': { 'edit_organisation_agreement', 'edit_organisation_billing_details', - 'edit_organisation_crown_status', 'edit_organisation_domains', 'edit_organisation_email_branding', 'edit_organisation_domains', diff --git a/app/notify_client/contact_list_api_client.py b/app/notify_client/contact_list_api_client.py deleted file mode 100644 index 9f7c04dc7..000000000 --- a/app/notify_client/contact_list_api_client.py +++ /dev/null @@ -1,37 +0,0 @@ -from app.notify_client import NotifyAdminAPIClient, _attach_current_user - - -class ContactListApiClient(NotifyAdminAPIClient): - - def create_contact_list( - self, - *, - service_id, - upload_id, - original_file_name, - row_count, - template_type, - ): - data = { - "id": upload_id, - "original_file_name": original_file_name, - "row_count": row_count, - "template_type": template_type, - } - - data = _attach_current_user(data) - job = self.post(url='/service/{}/contact-list'.format(service_id), data=data) - - return job - - def get_contact_lists(self, service_id): - return self.get(f'/service/{service_id}/contact-list') - - def get_contact_list(self, *, service_id, contact_list_id): - return self.get(f'/service/{service_id}/contact-list/{contact_list_id}') - - def delete_contact_list(self, *, service_id, contact_list_id): - return self.delete(f'/service/{service_id}/contact-list/{contact_list_id}') - - -contact_list_api_client = ContactListApiClient() diff --git a/app/notify_client/job_api_client.py b/app/notify_client/job_api_client.py index 7fd8af213..c54220657 100644 --- a/app/notify_client/job_api_client.py +++ b/app/notify_client/job_api_client.py @@ -25,14 +25,12 @@ class JobApiClient(NotifyAdminAPIClient): return job - def get_jobs(self, service_id, *, limit_days=None, contact_list_id=None, statuses=None, page=1): + def get_jobs(self, service_id, *, limit_days=None, statuses=None, page=1): params = {'page': page} if limit_days is not None: params['limit_days'] = limit_days if statuses is not None: params['statuses'] = ','.join(statuses) - if contact_list_id is not None: - params['contact_list_id'] = contact_list_id return self.get(url='/service/{}/job'.format(service_id), params=params) @@ -53,12 +51,11 @@ class JobApiClient(NotifyAdminAPIClient): if job['job_status'] != 'cancelled' ) - def get_page_of_jobs(self, service_id, *, page, statuses=None, contact_list_id=None, limit_days=None): + def get_page_of_jobs(self, service_id, *, page, statuses=None, limit_days=None): return self.get_jobs( service_id, statuses=statuses or self.NON_SCHEDULED_JOB_STATUSES, page=page, - contact_list_id=contact_list_id, limit_days=limit_days, ) @@ -88,15 +85,12 @@ class JobApiClient(NotifyAdminAPIClient): def has_jobs(self, service_id): return bool(self.get_jobs(service_id)['data']) - def create_job(self, job_id, service_id, scheduled_for=None, contact_list_id=None): + def create_job(self, job_id, service_id, scheduled_for=None): data = {"id": job_id} if scheduled_for: data.update({'scheduled_for': scheduled_for}) - if contact_list_id: - data.update({'contact_list_id': contact_list_id}) - data = _attach_current_user(data) job = self.post(url='/service/{}/job'.format(service_id), data=data) diff --git a/app/notify_client/organisations_api_client.py b/app/notify_client/organisations_api_client.py index c073ba9b2..85128dc28 100644 --- a/app/notify_client/organisations_api_client.py +++ b/app/notify_client/organisations_api_client.py @@ -37,12 +37,11 @@ class OrganisationsClient(NotifyAdminAPIClient): raise error @cache.delete('organisations') - def create_organisation(self, name, crown, organisation_type, agreement_signed): + def create_organisation(self, name, organisation_type, agreement_signed): return self.post( url="/organisations", data={ "name": name, - "crown": crown, "organisation_type": organisation_type, "agreement_signed": agreement_signed, } diff --git a/app/s3_client/s3_mou_client.py b/app/s3_client/s3_mou_client.py index b18c5c968..108d23758 100644 --- a/app/s3_client/s3_mou_client.py +++ b/app/s3_client/s3_mou_client.py @@ -4,17 +4,15 @@ from flask import current_app from app.s3_client.s3_logo_client import get_s3_object -def get_mou(organisation_is_crown): +def get_mou(): bucket = current_app.config['MOU_BUCKET_NAME'] - filename = 'crown.pdf' if organisation_is_crown else 'non-crown.pdf' - attachment_filename = 'U.S. Notify data sharing and financial agreement{}.pdf'.format( - '' if organisation_is_crown else ' (non-crown)' - ) + filename = 'agreement.pdf' + attachment_filename = 'U.S. Notify data sharing and financial agreement.pdf' try: key = get_s3_object(bucket, filename) return { 'path_or_file': key.get()['Body'], - 'attachment_filename': attachment_filename, + 'download_name': attachment_filename, 'as_attachment': True, } except botocore.exceptions.ClientError as exception: diff --git a/app/templates/components/uk_components/header/_header.scss b/app/templates/components/uk_components/header/_header.scss index ed1cbbb86..20ffec2a5 100644 --- a/app/templates/components/uk_components/header/_header.scss +++ b/app/templates/components/uk_components/header/_header.scss @@ -45,13 +45,7 @@ margin-right: govuk-spacing(1); } - .govuk-header__logotype-crown { - margin-right: 1px; - fill: currentColor; - vertical-align: middle; - } - - .govuk-header__logotype-crown-fallback-image { + .govuk-header__logotype-fallback-image { width: 36px; height: 32px; border: 0; @@ -269,8 +263,8 @@ background: transparent; } - // Hide the inverted crown when printing in browsers that don't support SVG. - .govuk-header__logotype-crown-fallback-image { + // Hide the inverted logo when printing in browsers that don't support SVG. + .govuk-header__logotype-fallback-image { display: none; } @@ -289,7 +283,7 @@ // Begin adjustments for font baseline offset // These should be removed when the font is updated with the correct baseline - .govuk-header__logotype-crown { + .govuk-header__logotype { position: relative; top: -4px; } diff --git a/app/templates/components/uk_components/header/template.njk b/app/templates/components/uk_components/header/template.njk index 1cd625c83..c4097c5b0 100644 --- a/app/templates/components/uk_components/header/template.njk +++ b/app/templates/components/uk_components/header/template.njk @@ -5,7 +5,7 @@