diff --git a/app/main/forms.py b/app/main/forms.py index 701b4a89f..0c607ce1b 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -243,9 +243,9 @@ class ForgivingIntegerField(StringField): return super().__call__(value=value, **kwargs) -def organisation_type(): +def organisation_type(label='Who runs this service?'): return RadioField( - 'Who runs this service?', + label, choices=[ ('central', 'Central government'), ('local', 'Local government'), @@ -530,6 +530,62 @@ class RenameOrganisationForm(StripWhitespaceForm): ]) +class OrganisationOrganisationTypeForm(StripWhitespaceForm): + organisation_type = organisation_type(label='What type of organisation is this?') + + +class OrganisationCrownStatusForm(StripWhitespaceForm): + crown_status = RadioField( + ( + 'Is this organisation a crown body?' + ), + choices=[ + ('crown', 'Yes'), + ('non-crown', 'No'), + ('unknown', 'Not sure'), + ], + validators=[ + DataRequired(message='Can’t be empty') + ], + ) + + +class OrganisationAgreementSignedForm(StripWhitespaceForm): + agreement_signed = RadioField( + ( + 'Has this organisation signed the agreement?' + ), + choices=[ + ('yes', 'Yes'), + ('no', 'No'), + ('unknown', 'No (but we have some service-specific agreements in place)'), + ], + validators=[ + DataRequired(message='Can’t be empty') + ], + ) + + +class OrganisationDomainsForm(StripWhitespaceForm): + + def populate(self, domains_list): + for index, value in enumerate(domains_list): + self.domains[index].data = value + + domains = FieldList( + StripWhitespaceStringField( + '', + validators=[ + Optional(), + ], + default='' + ), + min_entries=20, + max_entries=20, + label="Domain names" + ) + + class CreateServiceForm(StripWhitespaceForm): name = StringField( u'What’s your service called?', @@ -895,7 +951,7 @@ class ServiceSwitchChannelForm(ServiceOnOffSettingForm): super().__init__(name, *args, **kwargs) -class ServiceSetEmailBranding(StripWhitespaceForm): +class SetEmailBranding(StripWhitespaceForm): branding_style = RadioFieldWithNoneOption( 'Branding style', @@ -920,12 +976,12 @@ class ServiceSetEmailBranding(StripWhitespaceForm): ) -class ServiceSetLetterBranding(ServiceSetEmailBranding): +class SetLetterBranding(SetEmailBranding): # form is the same, but instead of GOV.UK we have None as a valid option DEFAULT = (FieldWithNoneOption.NONE_OPTION_VALUE, 'None') -class ServicePreviewBranding(StripWhitespaceForm): +class PreviewBranding(StripWhitespaceForm): branding_style = HiddenFieldWithNoneOption('branding_style') diff --git a/app/main/views/organisations.py b/app/main/views/organisations.py index 4a0ba7065..c59e103ac 100644 --- a/app/main/views/organisations.py +++ b/app/main/views/organisations.py @@ -1,3 +1,5 @@ +from collections import OrderedDict + from flask import flash, redirect, render_template, request, session, url_for from flask_login import current_user, login_required from notifications_python_client.errors import HTTPError @@ -5,6 +7,8 @@ from werkzeug.exceptions import abort from app import ( current_organisation, + email_branding_client, + letter_branding_client, org_invite_api_client, organisations_client, user_api_client, @@ -14,9 +18,18 @@ from app.main.forms import ( ConfirmPasswordForm, CreateOrUpdateOrganisation, InviteOrgUserForm, + OrganisationAgreementSignedForm, + OrganisationCrownStatusForm, + OrganisationDomainsForm, + OrganisationOrganisationTypeForm, + PreviewBranding, RenameOrganisationForm, + SearchByNameForm, SearchUsersForm, + SetEmailBranding, + SetLetterBranding, ) +from app.main.views.service_settings import get_branding_as_value_and_label from app.utils import user_has_permissions, user_is_platform_admin @@ -28,7 +41,8 @@ def organisations(): return render_template( 'views/organisations/index.html', - organisations=orgs + organisations=orgs, + search_form=SearchByNameForm(), ) @@ -165,8 +179,25 @@ def cancel_invited_org_user(org_id, invited_user_id): @login_required @user_has_permissions() def organisation_settings(org_id): + + email_branding = 'GOV.UK' + + if current_organisation['email_branding_id']: + email_branding = email_branding_client.get_email_branding( + current_organisation['email_branding_id'] + )['email_branding']['name'] + + letter_branding = None + + if current_organisation['letter_branding_id']: + letter_branding = letter_branding_client.get_letter_branding( + current_organisation['letter_branding_id'] + )['name'] + return render_template( 'views/organisations/organisation/settings/index.html', + email_branding=email_branding, + letter_branding=letter_branding, ) @@ -193,6 +224,215 @@ def edit_organisation_name(org_id): ) +@main.route("/organisations//settings/edit-type", methods=['GET', 'POST']) +@login_required +@user_has_permissions() +@user_is_platform_admin +def edit_organisation_type(org_id): + + form = OrganisationOrganisationTypeForm( + organisation_type=current_organisation['organisation_type'] + ) + + if form.validate_on_submit(): + organisations_client.update_organisation( + current_organisation['id'], + organisation_type=form.organisation_type.data, + ) + return redirect(url_for('.organisation_settings', org_id=org_id)) + + return render_template( + 'views/organisations/organisation/settings/edit-type.html', + form=form, + ) + + +@main.route("/organisations//settings/edit-crown-status", methods=['GET', 'POST']) +@login_required +@user_has_permissions() +@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']) +@login_required +@user_has_permissions() +@user_is_platform_admin +def edit_organisation_agreement(org_id): + + form = OrganisationAgreementSignedForm( + agreement_signed={ + True: 'yes', + False: 'no', + None: 'unknown', + }.get(current_organisation['agreement_signed']) + ) + + if form.validate_on_submit(): + organisations_client.update_organisation( + current_organisation['id'], + agreement_signed={ + 'yes': True, + 'no': False, + 'unknown': None, + }.get(form.agreement_signed.data), + ) + return redirect(url_for('.organisation_settings', org_id=org_id)) + + return render_template( + 'views/organisations/organisation/settings/edit-agreement.html', + form=form, + ) + + +@main.route("/organisations//settings/set-email-branding", methods=['GET', 'POST']) +@login_required +@user_has_permissions() +@user_is_platform_admin +def edit_organisation_email_branding(org_id): + + email_branding = email_branding_client.get_all_email_branding() + + form = SetEmailBranding( + all_branding_options=get_branding_as_value_and_label(email_branding), + current_branding=current_organisation['email_branding_id'], + ) + + if form.validate_on_submit(): + return redirect(url_for( + '.organisation_preview_email_branding', + org_id=org_id, + branding_style=form.branding_style.data, + )) + + return render_template( + 'views/organisations/organisation/settings/set-email-branding.html', + form=form, + search_form=SearchByNameForm() + ) + + +@main.route("/organisations//settings/preview-email-branding", methods=['GET', 'POST']) +@login_required +@user_has_permissions() +@user_is_platform_admin +def organisation_preview_email_branding(org_id): + + branding_style = request.args.get('branding_style', None) + + form = PreviewBranding(branding_style=branding_style) + + if form.validate_on_submit(): + organisations_client.update_organisation( + org_id, + email_branding_id=form.branding_style.data + ) + return redirect(url_for('.organisation_settings', org_id=org_id)) + + return render_template( + 'views/organisations/organisation/settings/preview-email-branding.html', + form=form, + action=url_for('main.organisation_preview_email_branding', org_id=org_id), + ) + + +@main.route("/organisations//settings/set-letter-branding", methods=['GET', 'POST']) +@login_required +@user_is_platform_admin +def edit_organisation_letter_branding(org_id): + letter_branding = letter_branding_client.get_all_letter_branding() + + form = SetLetterBranding( + 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']) +@login_required +@user_is_platform_admin +def organisation_preview_letter_branding(org_id): + branding_style = request.args.get('branding_style') + + form = PreviewBranding(branding_style=branding_style) + + if form.validate_on_submit(): + organisations_client.update_organisation( + org_id, + letter_branding_id=form.branding_style.data + ) + 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']) +@login_required +@user_has_permissions() +@user_is_platform_admin +def edit_organisation_domains(org_id): + + form = OrganisationDomainsForm() + + if form.validate_on_submit(): + organisations_client.update_organisation( + org_id, + domains=list(OrderedDict.fromkeys( + domain.lower() + for domain in filter(None, form.domains.data) + )), + ) + return redirect(url_for('.organisation_settings', org_id=org_id)) + + form.populate(current_organisation.get('domains', [])) + + return render_template( + 'views/organisations/organisation/settings/edit-domains.html', + form=form, + ) + + @main.route("/organisations//settings/edit-name/confirm", methods=['GET', 'POST']) @login_required @user_has_permissions() diff --git a/app/main/views/service_settings.py b/app/main/views/service_settings.py index f8b7822ae..ab8f17337 100644 --- a/app/main/views/service_settings.py +++ b/app/main/views/service_settings.py @@ -35,6 +35,7 @@ from app.main.forms import ( InternationalSMSForm, LinkOrganisationsForm, OrganisationTypeForm, + PreviewBranding, RenameServiceForm, SearchByNameForm, ServiceContactDetailsForm, @@ -44,12 +45,11 @@ from app.main.forms import ( ServiceInboundNumberForm, ServiceLetterContactBlockForm, ServiceOnOffSettingForm, - ServicePreviewBranding, ServiceReplyToEmailForm, - ServiceSetEmailBranding, - ServiceSetLetterBranding, ServiceSmsSenderForm, ServiceSwitchChannelForm, + SetEmailBranding, + SetLetterBranding, SMSPrefixForm, branding_options_dict, ) @@ -811,7 +811,7 @@ def set_free_sms_allowance(service_id): def service_set_email_branding(service_id): email_branding = email_branding_client.get_all_email_branding() - form = ServiceSetEmailBranding( + form = SetEmailBranding( all_branding_options=get_branding_as_value_and_label(email_branding), current_branding=current_service.email_branding_id, ) @@ -836,7 +836,7 @@ def service_set_email_branding(service_id): def service_preview_email_branding(service_id): branding_style = request.args.get('branding_style', None) - form = ServicePreviewBranding(branding_style=branding_style) + form = PreviewBranding(branding_style=branding_style) if form.validate_on_submit(): current_service.update( @@ -858,7 +858,7 @@ def service_preview_email_branding(service_id): def service_set_letter_branding(service_id): letter_branding = letter_branding_client.get_all_letter_branding() - form = ServiceSetLetterBranding( + form = SetLetterBranding( all_branding_options=get_branding_as_value_and_label(letter_branding), current_branding=current_service.letter_branding_id, ) @@ -883,7 +883,7 @@ def service_set_letter_branding(service_id): def service_preview_letter_branding(service_id): branding_style = request.args.get('branding_style') - form = ServicePreviewBranding(branding_style=branding_style) + form = PreviewBranding(branding_style=branding_style) if form.validate_on_submit(): current_service.update( diff --git a/app/navigation.py b/app/navigation.py index fd6f18711..2d6ce3a8c 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -154,7 +154,13 @@ class HeaderNavigation(Navigation): 'download_agreement', 'download_notifications_csv', 'edit_data_retention', + 'edit_organisation_agreement', + 'edit_organisation_crown_status', + 'edit_organisation_domains', + 'edit_organisation_email_branding', + 'edit_organisation_letter_branding', 'edit_organisation_name', + 'edit_organisation_type', 'edit_provider', 'edit_service_template', 'edit_template_postage', @@ -194,6 +200,8 @@ class HeaderNavigation(Navigation): 'old_using_notify', 'organisation_dashboard', 'organisation_settings', + 'organisation_preview_email_branding', + 'organisation_preview_letter_branding', 'privacy', 'public_agreement', 'public_download_agreement', @@ -428,7 +436,13 @@ class MainNavigation(Navigation): 'download_agreement', 'download_notifications_csv', 'edit_data_retention', + 'edit_organisation_agreement', + 'edit_organisation_crown_status', + 'edit_organisation_email_branding', + 'edit_organisation_domains', + 'edit_organisation_letter_branding', 'edit_organisation_name', + 'edit_organisation_type', 'edit_provider', 'edit_user_org_permissions', 'email_branding', @@ -462,6 +476,8 @@ class MainNavigation(Navigation): 'old_terms', 'old_using_notify', 'organisation_dashboard', + 'organisation_preview_email_branding', + 'organisation_preview_letter_branding', 'organisation_settings', 'organisations', 'platform_admin', @@ -590,6 +606,11 @@ class CaseworkNavigation(Navigation): 'choose_service', 'choose_template_to_copy', 'clear_cache', + 'edit_organisation_agreement', + 'edit_organisation_crown_status', + 'edit_organisation_domains', + 'edit_organisation_email_branding', + 'edit_organisation_letter_branding', 'confirm_edit_organisation_name', 'confirm_edit_user_email', 'confirm_edit_user_mobile_number', @@ -613,7 +634,11 @@ class CaseworkNavigation(Navigation): 'download_agreement', 'download_notifications_csv', 'edit_data_retention', + 'edit_organisation_agreement', + 'edit_organisation_crown_status', + 'edit_organisation_domains', 'edit_organisation_name', + 'edit_organisation_type', 'edit_provider', 'edit_service_template', 'edit_template_postage', @@ -659,6 +684,8 @@ class CaseworkNavigation(Navigation): 'old_terms', 'old_using_notify', 'organisation_dashboard', + 'organisation_preview_email_branding', + 'organisation_preview_letter_branding', 'organisation_settings', 'organisations', 'platform_admin', @@ -791,8 +818,18 @@ class OrgNavigation(Navigation): }, 'settings': { 'confirm_edit_organisation_name', + 'edit_organisation_agreement', + 'edit_organisation_crown_status', + 'edit_organisation_domains', + 'edit_organisation_email_branding', + 'edit_organisation_letter_branding', + 'edit_organisation_domains', 'edit_organisation_name', + 'edit_organisation_type', + 'organisation_preview_email_branding', + 'organisation_preview_letter_branding', 'organisation_settings', + }, 'team-members': { 'edit_user_org_permissions', diff --git a/app/notify_client/organisations_api_client.py b/app/notify_client/organisations_api_client.py index 60025856d..d1d082e82 100644 --- a/app/notify_client/organisations_api_client.py +++ b/app/notify_client/organisations_api_client.py @@ -15,11 +15,11 @@ class OrganisationsClient(NotifyAdminAPIClient): } return self.post(url="/organisations", data=data) + def update_organisation(self, org_id, **kwargs): + return self.post(url="/organisations/{}".format(org_id), data=kwargs) + def update_organisation_name(self, org_id, name): - data = { - "name": name - } - return self.post(url="/organisations/{}".format(org_id), data=data) + return self.update_organisation(org_id, name=name) def get_service_organisation(self, service_id): return self.get(url="/service/{}/organisation".format(service_id)) diff --git a/app/templates/org_nav.html b/app/templates/org_nav.html index 5544d4ec2..4c44b4892 100644 --- a/app/templates/org_nav.html +++ b/app/templates/org_nav.html @@ -1,6 +1,6 @@