mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-02 12:49:01 -04:00
Merge pull request #2209 from alphagov/add-email-branding-preview
Preview the email branding before setting it
This commit is contained in:
@@ -514,7 +514,7 @@ def useful_headers_after_request(response):
|
||||
"object-src 'self';"
|
||||
"font-src 'self' data:;"
|
||||
"img-src 'self' *.google-analytics.com *.notifications.service.gov.uk {} data:;"
|
||||
"frame-src www.youtube.com;".format(get_cdn_domain())
|
||||
"frame-src 'self' www.youtube.com;".format(get_cdn_domain())
|
||||
))
|
||||
if 'Cache-Control' in response.headers:
|
||||
del response.headers['Cache-Control']
|
||||
|
||||
43
app/assets/javascripts/emailPreviewPane.js
Normal file
43
app/assets/javascripts/emailPreviewPane.js
Normal file
@@ -0,0 +1,43 @@
|
||||
(function () {
|
||||
|
||||
'use strict';
|
||||
|
||||
const root = this,
|
||||
$ = this.jQuery;
|
||||
|
||||
let branding_type = $('.multiple-choice input[name="branding_type"]:checked');
|
||||
let branding_style = $('.multiple-choice input[name="branding_style"]:checked');
|
||||
|
||||
if (!branding_type.length || !branding_style.length) { return; }
|
||||
|
||||
branding_type = branding_type.val();
|
||||
branding_style = branding_style.val();
|
||||
|
||||
const $paneWrapper = $('<div class="column-full"></div>');
|
||||
const $form = $('form');
|
||||
const $previewPane = $('<iframe src="/_email?' +
|
||||
buildQueryString(['branding_type', branding_type], ['branding_style', branding_style]) +
|
||||
'" class="email-branding-preview"></iframe>');
|
||||
|
||||
function buildQueryString () {
|
||||
return $.map(arguments, (val, idx) => encodeURI(val[0]) + '=' + encodeURI(val[1])).join('&');
|
||||
}
|
||||
|
||||
function setPreviewPane (e) {
|
||||
const $target = $(e.target);
|
||||
if ($target.attr('name') == 'branding_type') {
|
||||
branding_type = $target.val();
|
||||
}
|
||||
if ($target.attr('name') == 'branding_style') {
|
||||
branding_style = $target.val();
|
||||
}
|
||||
$previewPane.attr('src', '/_email?' + buildQueryString(['branding_type', branding_type], ['branding_style', branding_style]));
|
||||
}
|
||||
|
||||
$paneWrapper.append($previewPane);
|
||||
$form.find('.grid-row').eq(0).prepend($paneWrapper);
|
||||
$form.attr('action', location.pathname.replace(/set-email-branding$/, 'preview-email-branding'));
|
||||
$form.find('button[type="submit"]').text('Save');
|
||||
|
||||
$('fieldset').on('change', 'input[name="branding_type"], input[name="branding_style"]', setPreviewPane);
|
||||
})();
|
||||
@@ -0,0 +1,7 @@
|
||||
.email-branding-preview {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: solid 1px $border-colour;
|
||||
min-height: 200px;
|
||||
margin-bottom: $gutter
|
||||
}
|
||||
@@ -61,6 +61,7 @@ $path: '/static/images/';
|
||||
@import 'components/conditional-radios';
|
||||
@import 'components/vendor/breadcrumbs';
|
||||
@import 'components/vendor/responsive-embed';
|
||||
@import 'components/email-preview-pane';
|
||||
|
||||
@import 'views/dashboard';
|
||||
@import 'views/users';
|
||||
|
||||
@@ -717,6 +717,12 @@ class ServiceSetBranding(StripWhitespaceForm):
|
||||
)
|
||||
|
||||
|
||||
class ServicePreviewBranding(StripWhitespaceForm):
|
||||
|
||||
branding_type = HiddenField('branding_type')
|
||||
branding_style = HiddenField('branding_style')
|
||||
|
||||
|
||||
class ServiceSelectEmailBranding(StripWhitespaceForm):
|
||||
|
||||
def __init__(self, email_brandings=[], *args, **kwargs):
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
from flask import abort, redirect, render_template, request, url_for
|
||||
from flask import (
|
||||
abort,
|
||||
make_response,
|
||||
redirect,
|
||||
render_template,
|
||||
request,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import current_user, login_required
|
||||
from notifications_utils.international_billing_rates import (
|
||||
INTERNATIONAL_BILLING_RATES,
|
||||
)
|
||||
from notifications_utils.template import HTMLEmailTemplate
|
||||
|
||||
from app import convert_to_boolean
|
||||
from app import email_branding_client
|
||||
from app.main import main
|
||||
from app.main.forms import SearchTemplatesForm
|
||||
from app.main.views.sub_navigation_dictionaries import features_nav
|
||||
from app.utils import AgreementInfo
|
||||
from app.utils import AgreementInfo, get_cdn_domain
|
||||
|
||||
|
||||
@main.route('/')
|
||||
@@ -73,45 +80,79 @@ def design_content():
|
||||
|
||||
@main.route('/_email')
|
||||
def email_template():
|
||||
return str(HTMLEmailTemplate({'subject': 'foo', '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\n'
|
||||
'It 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'
|
||||
'^ It is a long established fact that a reader will be distracted '
|
||||
'by the readable content of a page when looking at its layout.'
|
||||
'\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.'
|
||||
'\n\n\n'
|
||||
'1. One'
|
||||
'\n'
|
||||
'2. Two'
|
||||
'\n'
|
||||
'10. Three'
|
||||
'\n\n'
|
||||
'This is an example of an email sent using GOV.UK Notify.'
|
||||
'\n\n'
|
||||
'https://www.notifications.service.gov.uk'
|
||||
)}, govuk_banner=convert_to_boolean(request.args.get('govuk_banner', True))
|
||||
))
|
||||
branding_type = request.args.get('branding_type', 'govuk')
|
||||
branding_style = request.args.get('branding_style', 'None')
|
||||
|
||||
if branding_type == 'govuk' or branding_style == 'None':
|
||||
brand_name = None
|
||||
brand_colour = None
|
||||
brand_logo = None
|
||||
govuk_banner = True
|
||||
brand_banner = False
|
||||
else:
|
||||
email_branding = email_branding_client.get_email_branding(branding_style)['email_branding']
|
||||
brand_name = email_branding['name']
|
||||
brand_colour = email_branding['colour']
|
||||
brand_logo = 'https://{}/{}'.format(get_cdn_domain(), email_branding['logo'])
|
||||
govuk_banner = branding_type in ['govuk', 'both']
|
||||
brand_banner = branding_type == 'org_banner'
|
||||
|
||||
template = {
|
||||
'subject': 'foo',
|
||||
'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\n'
|
||||
'It 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'
|
||||
'^ It is a long established fact that a reader will be distracted '
|
||||
'by the readable content of a page when looking at its layout.'
|
||||
'\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.'
|
||||
'\n\n\n'
|
||||
'1. One'
|
||||
'\n'
|
||||
'2. Two'
|
||||
'\n'
|
||||
'10. Three'
|
||||
'\n\n'
|
||||
'This is an example of an email sent using GOV.UK Notify.'
|
||||
'\n\n'
|
||||
'https://www.notifications.service.gov.uk'
|
||||
)
|
||||
}
|
||||
|
||||
if not bool(request.args):
|
||||
resp = make_response(str(HTMLEmailTemplate(template)))
|
||||
else:
|
||||
resp = make_response(str(HTMLEmailTemplate(
|
||||
template,
|
||||
govuk_banner=govuk_banner,
|
||||
brand_name=brand_name,
|
||||
brand_colour=brand_colour,
|
||||
brand_logo=brand_logo,
|
||||
brand_banner=brand_banner,
|
||||
)))
|
||||
|
||||
resp.headers['X-Frame-Options'] = 'SAMEORIGIN'
|
||||
return resp
|
||||
|
||||
|
||||
@main.route('/documentation')
|
||||
|
||||
@@ -41,6 +41,7 @@ from app.main.forms import (
|
||||
ServiceEditInboundNumberForm,
|
||||
ServiceInboundNumberForm,
|
||||
ServiceLetterContactBlockForm,
|
||||
ServicePreviewBranding,
|
||||
ServiceReplyToEmailForm,
|
||||
ServiceSetBranding,
|
||||
ServiceSmsSenderForm,
|
||||
@@ -895,12 +896,36 @@ def set_free_sms_allowance(service_id):
|
||||
@user_is_platform_admin
|
||||
def service_set_email_branding(service_id):
|
||||
email_branding = email_branding_client.get_all_email_branding()
|
||||
branding_type = current_service.get('branding')
|
||||
|
||||
form = ServiceSetBranding(branding_type=current_service.get('branding'))
|
||||
form = ServiceSetBranding(branding_type=branding_type)
|
||||
|
||||
# dynamically create org choices, including the null option
|
||||
form.branding_style.choices = [('None', 'None')] + get_branding_as_value_and_label(email_branding)
|
||||
|
||||
if form.validate_on_submit():
|
||||
branding_style = None if form.branding_style.data == 'None' else form.branding_style.data
|
||||
return redirect(url_for('.service_preview_email_branding', service_id=service_id,
|
||||
branding_type=form.branding_type.data, branding_style=branding_style))
|
||||
|
||||
form.branding_style.data = current_service['email_branding'] or 'None'
|
||||
|
||||
return render_template(
|
||||
'views/service-settings/set-email-branding.html',
|
||||
form=form,
|
||||
branding_dict=get_branding_as_dict(email_branding)
|
||||
)
|
||||
|
||||
|
||||
@main.route("/services/<service_id>/service-settings/preview-email-branding", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@user_is_platform_admin
|
||||
def service_preview_email_branding(service_id):
|
||||
branding_type = request.args.get('branding_type', None)
|
||||
branding_style = request.args.get('branding_style', None)
|
||||
|
||||
form = ServicePreviewBranding(branding_type=branding_type, branding_style=branding_style)
|
||||
|
||||
if form.validate_on_submit():
|
||||
branding_style = None if form.branding_style.data == 'None' else form.branding_style.data
|
||||
service_api_client.update_service(
|
||||
@@ -910,12 +935,11 @@ def service_set_email_branding(service_id):
|
||||
)
|
||||
return redirect(url_for('.service_settings', service_id=service_id))
|
||||
|
||||
form.branding_style.data = current_service.email_branding or 'None'
|
||||
|
||||
return render_template(
|
||||
'views/service-settings/set-email-branding.html',
|
||||
'views/service-settings/preview-email-branding.html',
|
||||
form=form,
|
||||
branding_dict=get_branding_as_dict(email_branding)
|
||||
service_id=service_id,
|
||||
action=url_for('main.service_preview_email_branding', service_id=service_id),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -218,6 +218,7 @@ class HeaderNavigation(Navigation):
|
||||
'service_letter_contact_details',
|
||||
'service_name_change',
|
||||
'service_name_change_confirm',
|
||||
'service_preview_email_branding',
|
||||
'service_set_auth_type',
|
||||
'service_set_basic_view',
|
||||
'service_set_contact_link',
|
||||
@@ -339,6 +340,7 @@ class MainNavigation(Navigation):
|
||||
'service_letter_contact_details',
|
||||
'service_name_change',
|
||||
'service_name_change_confirm',
|
||||
'service_preview_email_branding',
|
||||
'service_set_auth_type',
|
||||
'service_set_basic_view',
|
||||
'service_set_contact_link',
|
||||
@@ -661,6 +663,7 @@ class CaseworkNavigation(Navigation):
|
||||
'service_letter_contact_details',
|
||||
'service_name_change',
|
||||
'service_name_change_confirm',
|
||||
'service_preview_email_branding',
|
||||
'service_set_auth_type',
|
||||
'service_set_basic_view',
|
||||
'service_set_contact_link',
|
||||
@@ -887,6 +890,7 @@ class OrgNavigation(Navigation):
|
||||
'service_letter_contact_details',
|
||||
'service_name_change',
|
||||
'service_name_change_confirm',
|
||||
'service_preview_email_branding',
|
||||
'service_set_auth_type',
|
||||
'service_set_basic_view',
|
||||
'service_set_contact_link',
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends "views/platform-admin/_base_template.html" %}
|
||||
|
||||
{% block service_page_title %}
|
||||
Preview email branding
|
||||
{% endblock %}
|
||||
|
||||
{% block platform_admin_content %}
|
||||
|
||||
<h1 class="heading-large">Preview email branding</h1>
|
||||
<div class="grid-row">
|
||||
<div class="column-full">
|
||||
<iframe src="{{ url_for('main.email_template', branding_type=form.branding_type.data, branding_style=form.branding_style.data) }}" class="email-branding-preview"></iframe>
|
||||
<form method="post" action="{{ action }}">
|
||||
<div class="form-group">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="page-footer">
|
||||
<button type="submit" class="button">Save</button>
|
||||
<a class="page-footer-back-link" href="{{ url_for('main.service_settings', service_id=service_id) }}">Back to service settings</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -9,18 +9,24 @@
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<h1 class="heading-large">Set email branding</h1>
|
||||
<div class="grid-row">
|
||||
<div class="column-three-quarters">
|
||||
<form method="post">
|
||||
<form method="post">
|
||||
<div class="grid-row">
|
||||
<div class="column-one-half">
|
||||
{{ radios(form.branding_type) }}
|
||||
</div>
|
||||
<div class="column-one-half">
|
||||
{{ branding_radios(form.branding_style, branding_dict=branding_dict) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-row">
|
||||
<div class="column-three-quarters">
|
||||
{{ page_footer(
|
||||
'Save',
|
||||
'Preview',
|
||||
back_link=url_for('.service_settings', service_id=current_service.id),
|
||||
back_link_text='Back to settings'
|
||||
) }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -74,6 +74,7 @@ gulp.task('javascripts', () => gulp
|
||||
paths.src + 'javascripts/preventDuplicateFormSubmissions.js',
|
||||
paths.src + 'javascripts/fullscreenTable.js',
|
||||
paths.src + 'javascripts/conditionalRadios.js',
|
||||
paths.src + 'javascripts/emailPreviewPane.js',
|
||||
paths.src + 'javascripts/main.js'
|
||||
])
|
||||
.pipe(plugins.prettyerror())
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import re
|
||||
|
||||
import pytest
|
||||
from bs4 import BeautifulSoup
|
||||
from flask import url_for
|
||||
|
||||
|
||||
@@ -10,11 +13,73 @@ from flask import url_for
|
||||
)
|
||||
def test_renders(client, mocker, query_args, result):
|
||||
|
||||
mock_convert_to_boolean = mocker.patch('app.main.views.index.convert_to_boolean')
|
||||
mocker.patch('app.main.views.index.HTMLEmailTemplate.__str__', return_value='rendered')
|
||||
|
||||
response = client.get(url_for('main.email_template', **query_args))
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.get_data(as_text=True) == 'rendered'
|
||||
mock_convert_to_boolean.assert_called_once_with(result)
|
||||
|
||||
|
||||
def test_displays_govuk_branding_by_default(client):
|
||||
|
||||
response = client.get(url_for('main.email_template'))
|
||||
|
||||
page = BeautifulSoup(response.data.decode("utf-8"), "html.parser")
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
assert page.find("a", attrs={"href": "https://www.gov.uk"})
|
||||
|
||||
|
||||
def test_displays_govuk_branding(client):
|
||||
|
||||
response = client.get(url_for('main.email_template', branding_type="govuk", branding_style="1"))
|
||||
|
||||
page = BeautifulSoup(response.data.decode("utf-8"), "html.parser")
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
assert page.find("a", attrs={"href": "https://www.gov.uk"})
|
||||
|
||||
|
||||
def test_displays_both_branding(client, mock_get_email_branding):
|
||||
|
||||
response = client.get(url_for('main.email_template', branding_type="both", branding_style="1"))
|
||||
|
||||
page = BeautifulSoup(response.data.decode("utf-8"), "html.parser")
|
||||
|
||||
assert response.status_code == 200
|
||||
mock_get_email_branding.assert_called_once_with('1')
|
||||
|
||||
assert page.find("a", attrs={"href": "https://www.gov.uk"})
|
||||
assert page.find("img", attrs={"src": re.compile("example.png$")})
|
||||
|
||||
|
||||
def test_displays_org_branding(client, mock_get_email_branding):
|
||||
|
||||
response = client.get(url_for('main.email_template', branding_type="org", branding_style="1"))
|
||||
|
||||
page = BeautifulSoup(response.data.decode("utf-8"), "html.parser")
|
||||
|
||||
assert response.status_code == 200
|
||||
mock_get_email_branding.assert_called_once_with('1')
|
||||
|
||||
assert not page.find("a", attrs={"href": "https://www.gov.uk"})
|
||||
assert page.find("img", attrs={"src": re.compile("example.png")})
|
||||
assert not page.select("body > table > tr > td[bgcolor='#f00']")
|
||||
|
||||
|
||||
def test_displays_org_branding_with_banner(client, mock_get_email_branding):
|
||||
|
||||
response = client.get(url_for('main.email_template', branding_type="org_banner",
|
||||
branding_style="1"))
|
||||
|
||||
page = BeautifulSoup(response.data.decode("utf-8"), "html.parser")
|
||||
|
||||
assert response.status_code == 200
|
||||
mock_get_email_branding.assert_called_once_with('1')
|
||||
|
||||
assert not page.find("a", attrs={"href": "https://www.gov.uk"})
|
||||
assert page.find("img", attrs={"src": re.compile("example.png")})
|
||||
assert page.select("body > table > tr > td[bgcolor='#f00']")
|
||||
|
||||
@@ -14,7 +14,7 @@ def test_owasp_useful_headers_set(client, mocker):
|
||||
"object-src 'self';"
|
||||
"font-src 'self' data:;"
|
||||
"img-src 'self' *.google-analytics.com *.notifications.service.gov.uk static-logos.test.com data:;"
|
||||
"frame-src www.youtube.com;"
|
||||
"frame-src 'self' www.youtube.com;"
|
||||
)
|
||||
|
||||
|
||||
@@ -31,5 +31,5 @@ def test_headers_non_ascii_characters_are_replaced(client, mocker):
|
||||
"object-src 'self';"
|
||||
"font-src 'self' data:;"
|
||||
"img-src 'self' *.google-analytics.com *.notifications.service.gov.uk static-logos??.test.com data:;"
|
||||
"frame-src www.youtube.com;"
|
||||
"frame-src 'self' www.youtube.com;"
|
||||
)
|
||||
|
||||
@@ -1769,7 +1769,7 @@ def test_should_show_organisations(
|
||||
app.service_api_client.get_service.assert_called_once_with(service_one['id'])
|
||||
|
||||
|
||||
def test_should_set_branding_and_organisations(
|
||||
def test_should_send_branding_and_organisations_to_preview(
|
||||
logged_in_platform_admin_client,
|
||||
service_one,
|
||||
mock_get_all_email_branding,
|
||||
@@ -1781,17 +1781,58 @@ def test_should_set_branding_and_organisations(
|
||||
),
|
||||
data={
|
||||
'branding_type': 'org',
|
||||
'organisation': '1'
|
||||
'branding_style': '1'
|
||||
}
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.service_settings', service_id=service_one['id'], _external=True)
|
||||
assert response.location == url_for('main.service_preview_email_branding',
|
||||
service_id=service_one['id'], branding_type='org',
|
||||
branding_style='1', _external=True)
|
||||
|
||||
mock_get_all_email_branding.assert_called_once_with()
|
||||
|
||||
|
||||
def test_should_preview_email_branding(
|
||||
logged_in_platform_admin_client,
|
||||
service_one,
|
||||
):
|
||||
response = logged_in_platform_admin_client.get(url_for(
|
||||
'main.service_preview_email_branding', service_id=service_one['id'],
|
||||
branding_type='org', branding_style='1'
|
||||
))
|
||||
assert response.status_code == 200
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
iframe = page.find('iframe', attrs={"class": "email-branding-preview"})
|
||||
|
||||
assert page.find('input', attrs={"id": "branding_type"})['value'] == 'org'
|
||||
assert page.find('input', attrs={"id": "branding_style"})['value'] == '1'
|
||||
assert iframe and iframe['src'] == '/_email?branding_type=org&branding_style=1'
|
||||
|
||||
app.service_api_client.get_service.assert_called_once_with(service_one['id'])
|
||||
|
||||
|
||||
def test_should_set_branding_and_organisations(
|
||||
logged_in_platform_admin_client,
|
||||
service_one,
|
||||
mock_update_service,
|
||||
):
|
||||
response = logged_in_platform_admin_client.post(
|
||||
url_for(
|
||||
'main.service_preview_email_branding', service_id=service_one['id']
|
||||
),
|
||||
data={
|
||||
'branding_type': 'org',
|
||||
'branding_style': '1'
|
||||
}
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.service_settings',
|
||||
service_id=service_one['id'], _external=True)
|
||||
|
||||
mock_update_service.assert_called_once_with(
|
||||
service_one['id'],
|
||||
branding='org',
|
||||
email_branding=None
|
||||
email_branding='1'
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user