diff --git a/app/__init__.py b/app/__init__.py index de4cc583c..37781f2ef 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -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'] diff --git a/app/assets/javascripts/emailPreviewPane.js b/app/assets/javascripts/emailPreviewPane.js new file mode 100644 index 000000000..7e42e99ba --- /dev/null +++ b/app/assets/javascripts/emailPreviewPane.js @@ -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 = $('
'); + const $form = $('form'); + const $previewPane = $(''); + + 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); +})(); diff --git a/app/assets/stylesheets/components/email-preview-pane.scss b/app/assets/stylesheets/components/email-preview-pane.scss new file mode 100644 index 000000000..807069631 --- /dev/null +++ b/app/assets/stylesheets/components/email-preview-pane.scss @@ -0,0 +1,7 @@ +.email-branding-preview { + width: 100%; + box-sizing: border-box; + border: solid 1px $border-colour; + min-height: 200px; + margin-bottom: $gutter +} diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 43edbaed5..9f5955cae 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -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'; diff --git a/app/main/forms.py b/app/main/forms.py index 988afb8b8..c99a93319 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -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): diff --git a/app/main/views/index.py b/app/main/views/index.py index 77ba91b94..f0ee6f746 100644 --- a/app/main/views/index.py +++ b/app/main/views/index.py @@ -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') diff --git a/app/main/views/service_settings.py b/app/main/views/service_settings.py index fc1351cef..11b79cf49 100644 --- a/app/main/views/service_settings.py +++ b/app/main/views/service_settings.py @@ -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/