From 4f72cc20ef6db5ebf79f598586ba9bf6b31214df Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Tue, 6 Dec 2016 15:28:21 +0000 Subject: [PATCH 1/8] Use patched version of WeasyPrint Reasons here: https://github.com/quis/WeasyPrint/commit/99718c2caf768324a1f0c2fd29a438e0a850837d --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index f430f8f6e..3160bee8a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,9 @@ Flask==0.10.1 Flask-Script==2.0.5 Flask-WTF==0.11 Flask-Login==0.3.2 +Flask-WeasyPrint==0.5 +git+https://github.com/quis/WeasyPrint.git@older-html5lib#egg=WeasyPrint==0.33 +html5lib==0.9999999 credstash==1.8.0 boto3==1.3.0 Pygments==2.0.2 From d0f90eac7eaf679a6cb16003a9518b6348392e96 Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Sun, 27 Nov 2016 18:10:18 +0000 Subject: [PATCH 2/8] Add an endpoint for generating a PDF of a letter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previewing a letter is different to previewing an email or text message because: - a letter has a layout - the layout is fixed, ie it doesn’t depend on the user’s device - the ‘send yourself a test’ feature won’t be as useful because it has a lead time, so the feedback loop will be much longer For these reasons a HTML-only preview of the letter won’t be enough (we don’t think). A PDF is more appropriate because: - it can replicate the layout of the letter exactly - it is a print format, so the user could even print themselves a copy locally to get a feel for how it will look This commit makes use of Flask WeasyPrint [1] to take a HTML representation of the letter, convert it to a PDF and serve it back to the user. The actual work to generate the HTML and specify the layout is done in utils, same as we do for rendering other messages. 1. https://pythonhosted.org/Flask-WeasyPrint/ --- app/main/views/templates.py | 13 ++++++++++ docker/Dockerfile-build | 8 ++++++ tests/app/main/views/test_templates.py | 35 +++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/app/main/views/templates.py b/app/main/views/templates.py index 3146cbec0..f6505c830 100644 --- a/app/main/views/templates.py +++ b/app/main/views/templates.py @@ -3,10 +3,12 @@ from string import ascii_uppercase from flask import request, render_template, redirect, url_for, flash, abort from flask_login import login_required +from flask_weasyprint import HTML, render_pdf from dateutil.parser import parse from notifications_utils.template import Template from notifications_utils.recipients import first_column_headings +from notifications_utils.renderers import LetterPreview from notifications_python_client.errors import HTTPError from app.main import main @@ -51,6 +53,17 @@ def view_template(service_id, template_id): ) +@main.route("/services//templates/.pdf") +@login_required +@user_has_permissions('view_activity', admin_override=True) +def view_letter_template_as_pdf(service_id, template_id): + template = Template( + service_api_client.get_service_template(service_id, template_id)['data'], + renderer=LetterPreview() + ) + return render_pdf(HTML(string=template.rendered)) + + @main.route("/services//templates//version/") @login_required @user_has_permissions( diff --git a/docker/Dockerfile-build b/docker/Dockerfile-build index 4c665c157..b58e945a2 100644 --- a/docker/Dockerfile-build +++ b/docker/Dockerfile-build @@ -26,6 +26,14 @@ RUN \ python-dev \ libffi-dev \ libssl-dev \ + libexif-dev \ + libfreetype6-dev \ + libjpeg-dev \ + liblcms2-2 \ + libtiff5-dev \ + zlib1g-dev \ + libpango1.0-dev \ + libcairo2-dev \ && echo "Install nodejs" \ && cd /tmp \ diff --git a/tests/app/main/views/test_templates.py b/tests/app/main/views/test_templates.py index 7320b6e24..9d7951a59 100644 --- a/tests/app/main/views/test_templates.py +++ b/tests/app/main/views/test_templates.py @@ -1,5 +1,6 @@ +from functools import partial from datetime import datetime -from unittest.mock import Mock +from unittest.mock import Mock, patch import pytest from bs4 import BeautifulSoup @@ -39,6 +40,38 @@ def test_should_show_page_for_one_template( service_id, template_id) +@pytest.mark.parametrize( + 'view, expected_content_type', + [ + ('.view_letter_template_as_pdf', 'application/pdf'), + ] +) +@patch("app.main.views.templates.LetterPreview.__call__") +def test_should_show_preview_letter_templates( + mock_letter_preview, + view, + expected_content_type, + client, + api_user_active, + mock_login, + mock_get_service, + mock_get_service_template, + mock_get_user, + mock_get_user_by_email, + mock_has_permissions, + fake_uuid +): + client.login(api_user_active) + service_id = fake_uuid + template_id = fake_uuid + response = client.get(url_for(view, service_id=service_id, template_id=template_id)) + + assert response.status_code == 200 + assert response.content_type == expected_content_type + mock_get_service_template.assert_called_with(service_id, template_id) + assert mock_letter_preview.call_args[0][0]['content'] == "Your vehicle tax is about to expire" + + def test_should_redirect_when_saving_a_template(app_, api_user_active, mock_login, From 43296469d6172bcccb825de95b2509373da1c600 Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Mon, 28 Nov 2016 11:01:39 +0000 Subject: [PATCH 3/8] Add endpoint for generating an image of a letter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PDF preview is all good, but it’s hard, finickeity and feels dirty to embed a PDF in a web page. It’s a more natural thing to embed an image in a web page. So this commit adds another endpoint to return an image of a letter template. It generates this image from the PDF preview, so the stack looks like: 1. `template.png` (generated in admin) 2. `template.pdf` (generated in admin) 3. HTML preview (generated by a `Renderer` in utils) 4. `Template` instance 5. serialised template from API 6. Template stored in database The library used to convert the PDF to an image is Wand[1], which binds to ImageMagick underneath. So in order to get this working locally on a Mac you will probably need to do: `brew install imagemagick ghostscript cairo pango`. To get it working on Ubuntu/EC2 is an exercise left to the reader… 1. http://docs.wand-py.org/en/0.4.4/ --- README.md | 2 +- app/assets/stylesheets/components/letter.scss | 13 ++++++++++++- app/main/views/templates.py | 18 +++++++++++++++++- docker/Dockerfile-build | 2 ++ requirements.txt | 1 + tests/app/main/views/test_templates.py | 1 + 6 files changed, 34 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5abc197e7..30d3f7d2a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Languages needed - [Node](https://nodejs.org/) 5.0.0 or greater - [npm](https://www.npmjs.com/) 3.0.0 or greater ```shell - brew install node + brew install node imagemagick ghostscript cairo pango ``` [NPM](npmjs.org) is Node's package management tool. `n` is a tool for managing diff --git a/app/assets/stylesheets/components/letter.scss b/app/assets/stylesheets/components/letter.scss index 009c53286..6af2a50cf 100644 --- a/app/assets/stylesheets/components/letter.scss +++ b/app/assets/stylesheets/components/letter.scss @@ -1,6 +1,7 @@ $outline-width: 5px; .letter { + font-family: Helvetica, Arial, sans-serif; box-shadow: 1px 1px 0 0 $panel-colour, @@ -8,6 +9,16 @@ $outline-width: 5px; -1px 1px 0 0 $panel-colour, -2px 2px 0 0 rgba($panel-colour, 0.5); outline: $outline-width solid rgba($text-colour, 0.1); - padding: 20px; + padding: 0; margin: $outline-width $outline-width $gutter; + + a { + display: block; + } + + img { + display: block; + max-width: 100%; + } + } diff --git a/app/main/views/templates.py b/app/main/views/templates.py index f6505c830..e1d67cedc 100644 --- a/app/main/views/templates.py +++ b/app/main/views/templates.py @@ -1,10 +1,12 @@ from datetime import datetime, timedelta +from io import BytesIO from string import ascii_uppercase -from flask import request, render_template, redirect, url_for, flash, abort +from flask import request, render_template, redirect, url_for, flash, abort, send_file from flask_login import login_required from flask_weasyprint import HTML, render_pdf from dateutil.parser import parse +from wand.image import Image from notifications_utils.template import Template from notifications_utils.recipients import first_column_headings @@ -64,6 +66,20 @@ def view_letter_template_as_pdf(service_id, template_id): return render_pdf(HTML(string=template.rendered)) +@main.route("/services//templates/.png") +@login_required +@user_has_permissions('view_activity', admin_override=True) +def view_letter_template_as_image(service_id, template_id): + output = BytesIO() + with Image( + blob=view_letter_template_as_pdf(service_id, template_id).get_data() + ) as image: + with image.convert('png') as converted: + converted.save(file=output) + output.seek(0) + return send_file(output, mimetype='image/png') + + @main.route("/services//templates//version/") @login_required @user_has_permissions( diff --git a/docker/Dockerfile-build b/docker/Dockerfile-build index b58e945a2..786737a10 100644 --- a/docker/Dockerfile-build +++ b/docker/Dockerfile-build @@ -34,6 +34,8 @@ RUN \ zlib1g-dev \ libpango1.0-dev \ libcairo2-dev \ + libmagickwand-dev \ + ghostscript \ && echo "Install nodejs" \ && cd /tmp \ diff --git a/requirements.txt b/requirements.txt index 3160bee8a..19c26785b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,6 +18,7 @@ pyexcel-xls==0.1.0 pyexcel-xlsx==0.1.0 pyexcel-ods3==0.1.1 pytz==2016.4 +wand==0.4.4 git+https://github.com/alphagov/notifications-python-client.git@3.0.1#egg=notifications-python-client==3.0.1 diff --git a/tests/app/main/views/test_templates.py b/tests/app/main/views/test_templates.py index 9d7951a59..32b759886 100644 --- a/tests/app/main/views/test_templates.py +++ b/tests/app/main/views/test_templates.py @@ -44,6 +44,7 @@ def test_should_show_page_for_one_template( 'view, expected_content_type', [ ('.view_letter_template_as_pdf', 'application/pdf'), + ('.view_letter_template_as_image', 'image/png'), ] ) @patch("app.main.views.templates.LetterPreview.__call__") From ae356fc7417ba1f3a10b01e46cc374dc0e2a35f2 Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Wed, 16 Nov 2016 17:59:47 +0000 Subject: [PATCH 4/8] Use PDF link renderer to preview letters Depends on and implements: - [x] https://github.com/alphagov/notifications-utils/pull/84 - [ ] https://github.com/alphagov/notifications-utils/pull/86 --- app/main/views/send.py | 1 + app/utils.py | 4 ++-- requirements.txt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/main/views/send.py b/app/main/views/send.py index 281ce0d46..fc0c8e5e1 100644 --- a/app/main/views/send.py +++ b/app/main/views/send.py @@ -20,6 +20,7 @@ from flask_login import login_required, current_user from notifications_utils.columns import Columns from notifications_utils.template import Template from notifications_utils.recipients import RecipientCSV, first_column_headings, validate_and_format_phone_number +from notifications_utils.renderers import EmailPreview, SMSPreview, LetterPDFLink from app.main import main from app.main.forms import CsvUploadForm, ChooseTimeForm, get_next_days_until, get_furthest_possible_scheduled_time diff --git a/app/utils.py b/app/utils.py index 367d09cfc..bba748c2b 100644 --- a/app/utils.py +++ b/app/utils.py @@ -8,7 +8,7 @@ import unicodedata from flask import (abort, current_app, session, request, redirect, url_for) from flask_login import current_user -from notifications_utils.renderers import SMSPreview, EmailPreview, LetterPreview +from notifications_utils.renderers import SMSPreview, EmailPreview, LetterPDFLink import pyexcel import pyexcel.ext.io @@ -233,5 +233,5 @@ def get_renderer(template_type, service, show_recipient, expand_emails=False): sender=service['sms_sender'], show_recipient=show_recipient ), - 'letter': LetterPreview(), + 'letter': LetterPDFLink(service['id']), }[template_type] diff --git a/requirements.txt b/requirements.txt index 19c26785b..897f946fb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,4 +22,4 @@ wand==0.4.4 git+https://github.com/alphagov/notifications-python-client.git@3.0.1#egg=notifications-python-client==3.0.1 -git+https://github.com/alphagov/notifications-utils.git@11.0.2#egg=notifications-utils==11.0.2 +git+https://github.com/alphagov/notifications-utils.git@11.1.0#egg=notifications-utils==11.1.0 From dc63de0f4e6c2e62bc9354069370328c23ba83dd Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Thu, 8 Dec 2016 11:50:59 +0000 Subject: [PATCH 5/8] Incorporate breaking utils changes Brings in: - [ ] https://github.com/alphagov/notifications-utils/pull/94 --- app/main/views/index.py | 10 ++-- app/main/views/jobs.py | 9 ++- app/main/views/send.py | 44 +++++++------- app/main/views/templates.py | 59 ++++++++----------- app/templates/views/check.html | 2 +- app/templates/views/jobs/job.html | 2 +- app/templates/views/send-from-api.html | 2 +- app/templates/views/send-test.html | 2 +- app/templates/views/send.html | 2 +- app/templates/views/templates/_template.html | 2 +- .../views/templates/_template_history.html | 2 +- app/utils.py | 28 ++++++--- requirements.txt | 2 +- tests/app/main/views/test_email_preview.py | 16 +++-- tests/app/main/views/test_jobs.py | 2 +- tests/app/main/views/test_templates.py | 6 +- 16 files changed, 90 insertions(+), 100 deletions(-) diff --git a/app/main/views/index.py b/app/main/views/index.py index ca9cbe9cd..642876962 100644 --- a/app/main/views/index.py +++ b/app/main/views/index.py @@ -3,8 +3,7 @@ from app.main import main from app import convert_to_boolean from flask_login import (login_required, current_user) - -from notifications_utils.renderers import HTMLEmail +from notifications_utils.template import HTMLEmailTemplate @main.route('/') @@ -57,9 +56,7 @@ def terms(): @main.route('/_email') def email_template(): - return HTMLEmail( - govuk_banner=convert_to_boolean(request.args.get('govuk_banner', True)) - )( + 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 ' @@ -96,7 +93,8 @@ def email_template(): '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)) + )) @main.route('/documentation') diff --git a/app/main/views/jobs.py b/app/main/views/jobs.py index eed140221..f20978e6e 100644 --- a/app/main/views/jobs.py +++ b/app/main/views/jobs.py @@ -16,7 +16,6 @@ from flask import ( ) from flask_login import login_required from werkzeug.datastructures import MultiDict -from notifications_utils.template import Template from app import ( job_api_client, @@ -31,7 +30,8 @@ from app.utils import ( generate_previous_dict, user_has_permissions, generate_notifications_csv, - get_help_argument + get_help_argument, + get_template, ) from app.statistics_utils import add_rate_to_job @@ -108,14 +108,13 @@ def view_job(service_id, job_id): 'views/jobs/job.html', finished=(total_notifications == processed_notifications), uploaded_file_name=job['original_file_name'], - template=Template( + template=get_template( service_api_client.get_service_template( service_id=service_id, template_id=job['template'], version=job['template_version'] )['data'], - prefix=current_service['name'], - sms_sender=current_service['sms_sender'] + current_service, ), status=request.args.get('status', ''), updates_url=url_for( diff --git a/app/main/views/send.py b/app/main/views/send.py index fc0c8e5e1..b2aa846be 100644 --- a/app/main/views/send.py +++ b/app/main/views/send.py @@ -18,9 +18,7 @@ from flask import ( from flask_login import login_required, current_user from notifications_utils.columns import Columns -from notifications_utils.template import Template from notifications_utils.recipients import RecipientCSV, first_column_headings, validate_and_format_phone_number -from notifications_utils.renderers import EmailPreview, SMSPreview, LetterPDFLink from app.main import main from app.main.forms import CsvUploadForm, ChooseTimeForm, get_next_days_until, get_furthest_possible_scheduled_time @@ -29,7 +27,7 @@ from app.main.uploader import ( s3download ) from app import job_api_client, service_api_client, current_service, user_api_client -from app.utils import user_has_permissions, get_errors_for_csv, Spreadsheet, get_help_argument, get_renderer +from app.utils import user_has_permissions, get_errors_for_csv, Spreadsheet, get_help_argument, get_template def get_page_headings(template_type): @@ -89,10 +87,8 @@ def choose_template(service_id, template_type): return render_template( 'views/templates/choose.html', templates=[ - Template( - template, - renderer=get_renderer(template_type, current_service, show_recipient=False) - ) for template in service_api_client.get_service_templates(service_id)['data'] + get_template(template, current_service) + for template in service_api_client.get_service_templates(service_id)['data'] if template['template_type'] == template_type ], template_type=template_type, @@ -104,10 +100,12 @@ def choose_template(service_id, template_type): @login_required @user_has_permissions('send_texts', 'send_emails', 'send_letters') def send_messages(service_id, template_id): - template = Template( - service_api_client.get_service_template(service_id, template_id)['data'] + + template = get_template( + service_api_client.get_service_template(service_id, template_id)['data'], + current_service, + show_recipient=True ) - template.renderer = get_renderer(template.template_type, current_service, show_recipient=True) form = CsvUploadForm() if form.validate_on_submit(): @@ -145,7 +143,9 @@ def send_messages(service_id, template_id): @login_required @user_has_permissions('send_texts', 'send_emails', 'send_letters', 'manage_templates', any_=True) def get_example_csv(service_id, template_id): - template = Template(service_api_client.get_service_template(service_id, template_id)['data']) + template = get_template( + service_api_client.get_service_template(service_id, template_id)['data'], current_service + ) return Spreadsheet.from_rows([ first_column_headings[template.template_type] + list(template.placeholders), get_example_csv_rows(template) @@ -162,14 +162,12 @@ def send_test(service_id, template_id): file_name = current_app.config['TEST_MESSAGE_FILENAME'] - template = Template( + template = get_template( service_api_client.get_service_template(service_id, template_id)['data'], - prefix=current_service['name'], - sms_sender=current_service['sms_sender'] + current_service, + show_recipient=True ) - template.renderer = get_renderer(template.template_type, current_service, show_recipient=True) - if len(template.placeholders) == 0 or request.method == 'POST': upload_id = s3upload( service_id, @@ -209,10 +207,8 @@ def send_test(service_id, template_id): def send_from_api(service_id, template_id): return render_template( 'views/send-from-api.html', - template=Template( - service_api_client.get_service_template(service_id, template_id)['data'], - prefix=current_service['name'], - sms_sender=current_service['sms_sender'] + template=get_template( + service_api_client.get_service_template(service_id, template_id)['data'], current_service ) ) @@ -234,15 +230,15 @@ def check_messages(service_id, template_type, upload_id): if not contents: flash('There was a problem reading your upload file') - template = Template( + template = get_template( service_api_client.get_service_template( service_id, session['upload_data'].get('template_id') - )['data'] + )['data'], + current_service, + show_recipient=True ) - template.renderer = get_renderer(template_type, current_service, show_recipient=True) - recipients = RecipientCSV( contents, template_type=template.template_type, diff --git a/app/main/views/templates.py b/app/main/views/templates.py index e1d67cedc..fcbd14ef8 100644 --- a/app/main/views/templates.py +++ b/app/main/views/templates.py @@ -8,13 +8,12 @@ from flask_weasyprint import HTML, render_pdf from dateutil.parser import parse from wand.image import Image -from notifications_utils.template import Template +from notifications_utils.template import LetterPreviewTemplate from notifications_utils.recipients import first_column_headings -from notifications_utils.renderers import LetterPreview from notifications_python_client.errors import HTTPError from app.main import main -from app.utils import user_has_permissions, get_renderer +from app.utils import user_has_permissions, get_template from app.main.forms import SMSTemplateForm, EmailTemplateForm, LetterTemplateForm from app.main.views.send import get_example_csv_rows from app import service_api_client, current_service, template_statistics_client @@ -43,15 +42,13 @@ page_headings = { admin_override=True, any_=True ) def view_template(service_id, template_id): - template = Template( - service_api_client.get_service_template(service_id, template_id)['data'] - ) - template.renderer = get_renderer( - template.template_type, current_service, show_recipient=False, expand_emails=True - ) return render_template( 'views/templates/template.html', - template=template + template=get_template( + service_api_client.get_service_template(service_id, template_id)['data'], + current_service, + expand_emails=True + ) ) @@ -59,11 +56,11 @@ def view_template(service_id, template_id): @login_required @user_has_permissions('view_activity', admin_override=True) def view_letter_template_as_pdf(service_id, template_id): - template = Template( - service_api_client.get_service_template(service_id, template_id)['data'], - renderer=LetterPreview() - ) - return render_pdf(HTML(string=template.rendered)) + return render_pdf(HTML(string=str( + LetterPreviewTemplate( + service_api_client.get_service_template(service_id, template_id)['data'], + ) + ))) @main.route("/services//templates/.png") @@ -92,15 +89,13 @@ def view_letter_template_as_image(service_id, template_id): any_=True ) def view_template_version(service_id, template_id, version): - template = Template( - service_api_client.get_service_template(service_id, template_id, version)['data'] - ) - template.renderer = get_renderer( - template.template_type, current_service, show_recipient=False, expand_emails=True - ) return render_template( 'views/templates/template_history.html', - template=template + template=get_template( + service_api_client.get_service_template(service_id, template_id, version)['data'], + current_service, + expand_emails=True + ) ) @@ -154,14 +149,14 @@ def edit_service_template(service_id, template_id): if form.validate_on_submit(): subject = form.subject.data if hasattr(form, 'subject') else None - new_template = Template({ + new_template = get_template({ 'name': form.name.data, 'content': form.template_content.data, 'subject': subject, 'template_type': template['template_type'], 'id': template['id'] - }) - template_change = Template(template).compare_to(new_template) + }, current_service) + template_change = get_template(template, current_service).compare_to(new_template) if template_change.has_different_placeholders and not request.form.get('confirm'): return render_template( 'views/templates/breaking-change.html', @@ -260,18 +255,12 @@ def delete_service_template(service_id, template_id): any_=True ) def view_template_versions(service_id, template_id): - - versions = [] - for template in service_api_client.get_service_template_versions(service_id, template_id)['data']: - template = Template(template) - template.renderer = get_renderer( - template.template_type, current_service, show_recipient=False, expand_emails=True - ) - versions.append(template) - return render_template( 'views/templates/choose_history.html', - versions=versions + versions=[ + get_template(template, current_service, expand_emails=True) + for template in service_api_client.get_service_template_versions(service_id, template_id)['data'] + ] ) diff --git a/app/templates/views/check.html b/app/templates/views/check.html index 79b9527ca..0d013a4a2 100644 --- a/app/templates/views/check.html +++ b/app/templates/views/check.html @@ -127,7 +127,7 @@ {% endif %} - {{ template.rendered }} + {{ template|string }} {% if errors %} {% if request.args.from_test %} diff --git a/app/templates/views/jobs/job.html b/app/templates/views/jobs/job.html index 70388542d..4c40370b8 100644 --- a/app/templates/views/jobs/job.html +++ b/app/templates/views/jobs/job.html @@ -12,7 +12,7 @@ {{ uploaded_file_name }} - {{ template.rendered }} + {{ template|string }} {{ ajax_block(partials, updates_url, 'status', finished=finished) }} {{ ajax_block(partials, updates_url, 'counts', finished=finished) }} diff --git a/app/templates/views/send-from-api.html b/app/templates/views/send-from-api.html index 34bc0cd13..667316dcb 100644 --- a/app/templates/views/send-from-api.html +++ b/app/templates/views/send-from-api.html @@ -11,7 +11,7 @@ API info - {{ template.rendered }} + {{ template|string }}
{{ api_key(template.id, name="Template ID", thing='template ID') }} diff --git a/app/templates/views/send-test.html b/app/templates/views/send-test.html index 2c2820116..c801e08dc 100644 --- a/app/templates/views/send-test.html +++ b/app/templates/views/send-test.html @@ -19,7 +19,7 @@

Send yourself a test

{% endif %} - {{ template.rendered }} + {{ template|string }}
{% call(item, row_number) list_table( diff --git a/app/templates/views/send.html b/app/templates/views/send.html index e0a2eb9ba..847be9b0a 100644 --- a/app/templates/views/send.html +++ b/app/templates/views/send.html @@ -11,7 +11,7 @@

Upload recipients

- {{ template.rendered }} + {{ template|string }}