diff --git a/app/assets/images/branding-options/both.png b/app/assets/images/branding-options/both.png new file mode 100644 index 000000000..aec54023d Binary files /dev/null and b/app/assets/images/branding-options/both.png differ diff --git a/app/assets/images/branding-options/govuk.png b/app/assets/images/branding-options/govuk.png new file mode 100644 index 000000000..85c9b68a4 Binary files /dev/null and b/app/assets/images/branding-options/govuk.png differ diff --git a/app/assets/images/branding-options/org.png b/app/assets/images/branding-options/org.png new file mode 100644 index 000000000..b8b442583 Binary files /dev/null and b/app/assets/images/branding-options/org.png differ diff --git a/app/assets/images/branding-options/org_banner.png b/app/assets/images/branding-options/org_banner.png new file mode 100644 index 000000000..872255a38 Binary files /dev/null and b/app/assets/images/branding-options/org_banner.png differ diff --git a/app/assets/stylesheets/app.scss b/app/assets/stylesheets/app.scss index fe7b63bf4..7bc54d38f 100644 --- a/app/assets/stylesheets/app.scss +++ b/app/assets/stylesheets/app.scss @@ -241,3 +241,13 @@ details .arrow { } } + +.bordered-image { + padding: 5px; + outline: 1px solid $border-colour; + max-width: 100%; + + label & { + cursor: pointer; + } +} diff --git a/app/main/forms.py b/app/main/forms.py index 7fea38a96..cffa5e513 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -931,3 +931,23 @@ class LinkOrganisationsForm(StripWhitespaceForm): DataRequired() ] ) + + +branding_options = ( + ('govuk', 'GOV.UK only'), + ('both', 'GOV.UK and logo'), + ('org', 'Your logo'), + ('org_banner', 'Your logo on a colour'), +) +branding_options_dict = dict(branding_options) + + +class BrandingOptionsEmail(StripWhitespaceForm): + + options = RadioField( + 'Branding options', + choices=branding_options, + validators=[ + DataRequired() + ], + ) diff --git a/app/main/views/service_settings.py b/app/main/views/service_settings.py index 1aca03f3d..d74898004 100644 --- a/app/main/views/service_settings.py +++ b/app/main/views/service_settings.py @@ -25,6 +25,7 @@ from app import ( ) from app.main import main from app.main.forms import ( + BrandingOptionsEmail, ConfirmPasswordForm, FreeSMSAllowance, InternationalSMSForm, @@ -41,6 +42,7 @@ from app.main.forms import ( ServiceSmsSenderForm, ServiceSwitchLettersForm, SMSPrefixForm, + branding_options_dict, ) from app.utils import ( AgreementInfo, @@ -884,6 +886,44 @@ def link_service_to_organisation(service_id): ) +@main.route("/services//branding-request/email", methods=['GET', 'POST']) +@login_required +@user_has_permissions('manage_service') +def branding_request(service_id): + + form = BrandingOptionsEmail( + options=current_service['branding'] + ) + + if form.validate_on_submit(): + zendesk_client.create_ticket( + subject='Email branding request - {}'.format(current_service['name']), + message=( + 'On behalf of {} ({})\n' + '\n---' + '\nBranding requested: {}' + ).format( + current_service['name'], + url_for('main.service_dashboard', service_id=current_service['id'], _external=True), + branding_options_dict[form.options.data], + ), + ticket_type=zendesk_client.TYPE_QUESTION, + user_email=current_user.email_address, + user_name=current_user.name, + ) + + flash(( + 'Thanks for your branding request. We’ll get back to you ' + 'within one working day.' + ), 'default') + return redirect(url_for('.service_settings', service_id=service_id)) + + return render_template( + 'views/service-settings/branding/email-options.html', + form=form, + ) + + def get_branding_as_value_and_label(email_branding): return [ (branding['id'], branding['name']) diff --git a/app/navigation.py b/app/navigation.py index 2ce4a7fda..8d15a63da 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -109,6 +109,7 @@ class HeaderNavigation(Navigation): 'api_integration', 'api_keys', 'archive_service', + 'branding_request', 'callbacks', 'cancel_invited_org_user', 'cancel_invited_user', @@ -309,6 +310,7 @@ class MainNavigation(Navigation): 'usage', }, 'settings': { + 'branding_request', 'link_service_to_organisation', 'request_to_go_live', 'service_add_email_reply_to', @@ -519,6 +521,7 @@ class OrgNavigation(Navigation): 'api_keys', 'archive_service', 'bat_phone', + 'branding_request', 'callbacks', 'cancel_invited_org_user', 'cancel_invited_user', diff --git a/app/templates/components/radios.html b/app/templates/components/radios.html index c402006a3..3d62bdfa1 100644 --- a/app/templates/components/radios.html +++ b/app/templates/components/radios.html @@ -18,31 +18,36 @@ {% endif %} {% for option in field %} -
- - -
+ {{ radio(option, disable, option_hints) }} {% endfor %} {% endmacro %} +{% macro radio(option, disable=[], option_hints={}) %} +
+ + +
+{% endmacro %} + + {% macro radio_select( field, hint=None, diff --git a/app/templates/views/service-settings.html b/app/templates/views/service-settings.html index d54edd799..9e38b0866 100644 --- a/app/templates/views/service-settings.html +++ b/app/templates/views/service-settings.html @@ -89,6 +89,17 @@ }} {% endcall %} + {% call row() %} + {{ text_field('Email branding') }} + {{ text_field( + 'GOV.UK' if current_service.branding == 'govuk' else 'Your branding' + ) }} + {{ edit_field( + 'Change', + url_for('.branding_request', service_id=current_service.id), + )}} + {% endcall %} + {% endif %} {% endcall %} diff --git a/app/templates/views/service-settings/branding/email-options.html b/app/templates/views/service-settings/branding/email-options.html new file mode 100644 index 000000000..e9a93355f --- /dev/null +++ b/app/templates/views/service-settings/branding/email-options.html @@ -0,0 +1,46 @@ +{% extends "withnav_template.html" %} +{% from "components/radios.html" import radio %} +{% from "components/textbox.html" import textbox %} +{% from "components/page-footer.html" import page_footer %} + +{% block service_page_title %} + Email branding +{% endblock %} + +{% block maincolumn_content %} + +

Email branding

+ +
+
+ + Choose the branding you’d like on your emails. + + {% if form.options.errors %} + + You need to choose an option + + {% endif %} +
+ {% for option in form.options %} +
+ + {{ radio(option) }} +
+ {% endfor %} +
+
+

+ We’ll email you once your branding’s ready to use, or if we need any + more info. +

+ {{ page_footer( + 'Request new branding', + destructive=destructive, + back_link=url_for('.service_settings', service_id=current_service.id) + ) }} +
+ +{% endblock %} diff --git a/tests/app/main/views/test_service_settings.py b/tests/app/main/views/test_service_settings.py index e1ffe82cd..0ce3b0c1d 100644 --- a/tests/app/main/views/test_service_settings.py +++ b/tests/app/main/views/test_service_settings.py @@ -54,6 +54,7 @@ def mock_get_service_settings_page_common( 'Label Value Action', 'Send emails On Change', 'Email reply to addresses Not set Change', + 'Email branding GOV.UK Change', 'Label Value Action', 'Send text messages On Change', @@ -75,6 +76,7 @@ def mock_get_service_settings_page_common( 'Label Value Action', 'Send emails On Change', 'Email reply to addresses Not set Change', + 'Email branding GOV.UK Change', 'Label Value Action', 'Send text messages On Change', @@ -134,6 +136,7 @@ def test_should_show_overview( 'Label Value Action', 'Send emails On Change', 'Email reply to addresses test@example.com Manage', + 'Email branding GOV.UK Change', 'Label Value Action', 'Send text messages On Change', @@ -154,6 +157,7 @@ def test_should_show_overview( 'Label Value Action', 'Send emails On Change', 'Email reply to addresses test@example.com Manage', + 'Email branding GOV.UK Change', 'Label Value Action', 'Send text messages On Change', @@ -775,8 +779,8 @@ def test_and_more_hint_appears_on_settings_with_more_than_just_a_single_sender( ) assert get_row(page, 3) == "Email reply to addresses test@example.com …and 2 more Manage" - assert get_row(page, 5) == "Text message sender Example …and 2 more Manage" - assert get_row(page, 10) == "Sender addresses 1 Example Street …and 2 more Manage" + assert get_row(page, 6) == "Text message sender Example …and 2 more Manage" + assert get_row(page, 11) == "Sender addresses 1 Example Street …and 2 more Manage" @pytest.mark.parametrize('sender_list_page, expected_output', [ @@ -2385,3 +2389,73 @@ def test_update_service_organisation_does_not_update_if_same_value( assert response.status_code == 302 mock_update_service_organisation.called is False + + +def test_show_email_branding_request_page( + client_request, +): + page = client_request.get( + '.branding_request', service_id=SERVICE_ONE_ID + ) + + radios = page.select('input[type=radio]') + + for index, option in enumerate(( + 'govuk', + 'both', + 'org', + 'org_banner', + )): + assert radios[index]['name'] == 'options' + assert radios[index]['value'] == option + + +@pytest.mark.parametrize('choice, requested_branding', ( + ('govuk', 'GOV.UK only'), + ('both', 'GOV.UK and logo'), + ('org', 'Your logo'), + ('org_banner', 'Your logo on a colour'), + pytest.mark.xfail(('foo', 'Nope'), raises=AssertionError), +)) +def test_submit_email_branding_request( + client_request, + mocker, + choice, + requested_branding, + mock_get_service_settings_page_common, + no_reply_to_email_addresses, + no_letter_contact_blocks, + mock_get_service_organisation, + single_sms_sender, +): + + zendesk = mocker.patch( + 'app.main.views.service_settings.zendesk_client.create_ticket', + autospec=True, + ) + + page = client_request.post( + '.branding_request', service_id=SERVICE_ONE_ID, + _data={ + 'options': choice, + }, + _follow_redirects=True, + ) + + zendesk.assert_called_once_with( + message=( + 'On behalf of service one ' + '(http://localhost/services/596364a0-858e-42c8-9062-a8fe822260eb)\n' + '\n' + '---\n' + 'Branding requested: {}' + ).format(requested_branding), + subject='Email branding request - service one', + ticket_type='question', + user_email='test@user.gov.uk', + user_name='Test User', + ) + assert normalize_spaces(page.select_one('.banner-default').text) == ( + 'Thanks for your branding request. We’ll get back to you ' + 'within one working day.' + )