From 9d08e23a482b86bbf08eb8f05632fb0defeb9d77 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 4 Jan 2017 13:31:40 +0100 Subject: [PATCH] Fix #306 -- Add HTML multipart version to emails --- src/pretix/base/models/log.py | 1 + src/pretix/base/services/mail.py | 45 ++++- .../pretixbase/email/plainwrapper.html | 166 ++++++++++++++++++ src/requirements/production.txt | 3 + 4 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 src/pretix/base/templates/pretixbase/email/plainwrapper.html diff --git a/src/pretix/base/models/log.py b/src/pretix/base/models/log.py index de259e459..ddd797037 100644 --- a/src/pretix/base/models/log.py +++ b/src/pretix/base/models/log.py @@ -1,4 +1,5 @@ import json + from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models diff --git a/src/pretix/base/services/mail.py b/src/pretix/base/services/mail.py index 3a1b82a2e..5ad502709 100644 --- a/src/pretix/base/services/mail.py +++ b/src/pretix/base/services/mail.py @@ -1,10 +1,14 @@ import logging from typing import Any, Dict +import bleach +import cssutils +import markdown from django.conf import settings -from django.core.mail import EmailMessage, get_connection +from django.core.mail import EmailMultiAlternatives, get_connection from django.template.loader import get_template from django.utils.translation import ugettext as _ +from inlinestyler.utils import inline_css from pretix.base.i18n import LazyI18nString, language from pretix.base.models import Event, Order @@ -13,6 +17,7 @@ from pretix.multidomain.urlreverse import build_absolute_uri logger = logging.getLogger('pretix.base.mail') INVALID_ADDRESS = 'invalid-pretix-mail-address' +cssutils.log.setLevel(logging.CRITICAL) class TolerantDict(dict): @@ -63,25 +68,42 @@ def mail(email: str, subject: str, template: str, body = str(template) if context: body = body.format_map(TolerantDict(context)) + body_md = bleach.linkify(bleach.clean(markdown.markdown(body), tags=bleach.ALLOWED_TAGS + [ + 'p', + ])) else: tpl = get_template(template) body = tpl.render(context) + body_md = bleach.linkify(markdown.markdown(body)) sender = event.settings.get('mail_from') if event else settings.MAIL_FROM subject = str(subject) + body_plain = body + + htmlctx = { + 'site': settings.PRETIX_INSTANCE_NAME, + 'site_url': settings.SITE_URL, + 'body': body_md, + 'color': '#8E44B3' + } + if event: + htmlctx['event'] = event + htmlctx['color'] = event.settings.primary_color + prefix = event.settings.get('mail_prefix') if prefix: subject = "[%s] %s" % (prefix, subject) - body += "\r\n\r\n-- \r\n" - body += _( + body_plain += "\r\n\r\n-- \r\n" + body_plain += _( "You are receiving this email because you placed an order for {event}." ).format(event=event.name) if order: - body += "\r\n" - body += _( + htmlctx['order'] = order + body_plain += "\r\n" + body_plain += _( "You can view your order details at the following URL:\n{orderurl}." ).replace("\n", "\r\n").format( event=event.name, orderurl=build_absolute_uri( @@ -91,13 +113,18 @@ def mail(email: str, subject: str, template: str, } ) ) - body += "\r\n" - return mail_send([email], subject, body, sender, event.id if event else None, headers) + body_plain += "\r\n" + + tpl = get_template('pretixbase/email/plainwrapper.html') + body_html = tpl.render(htmlctx) + return mail_send([email], subject, body_plain, body_html, sender, event.id if event else None, headers) @app.task -def mail_send_task(to: str, subject: str, body: str, sender: str, event: int=None, headers: dict=None) -> bool: - email = EmailMessage(subject, body, sender, to=to, headers=headers) +def mail_send_task(to: str, subject: str, body: str, html: str, sender: str, + event: int=None, headers: dict=None) -> bool: + email = EmailMultiAlternatives(subject, body, sender, to=to, headers=headers) + email.attach_alternative(inline_css(html), "text/html") if event: event = Event.objects.get(id=event) backend = event.get_mail_backend() diff --git a/src/pretix/base/templates/pretixbase/email/plainwrapper.html b/src/pretix/base/templates/pretixbase/email/plainwrapper.html new file mode 100644 index 000000000..fb5be9a28 --- /dev/null +++ b/src/pretix/base/templates/pretixbase/email/plainwrapper.html @@ -0,0 +1,166 @@ +{% load eventurl %} +{% load i18n %} + + + + + + + + + + + + + + + + {% if order %} + + + + + + + {% endif %} + + + +
+ {% if event %} +

{{ event.name }}

+ {% else %} +

{{ site }}

+ {% endif %} +
+
+ {{ body|safe }} +
+
+
+ {% trans "You are receiving this email because you placed an order for the following event:" %}
+ {% trans "Event:" %} {{ event.name }}
+ {% trans "Order code:" %} {{ order.code }}
+ {% trans "Order date:" %} {{ order.datetime|date:"SHORT_DATE_FORMAT" }}
+ + {% trans "View order details" %} + +
+
+
+
+ + diff --git a/src/requirements/production.txt b/src/requirements/production.txt index 0e43d15b3..b67a7fb14 100644 --- a/src/requirements/production.txt +++ b/src/requirements/production.txt @@ -18,6 +18,7 @@ django-formtools==1.0 # https://github.com/celery/celery/pull/3199 git+https://github.com/pretix/celery.git@pretix#egg=celery django-statici18n==1.2.* +inlinestyler==0.2.* # Deployment / static file compilation requirements BeautifulSoup4 @@ -29,3 +30,5 @@ dj-static csscompressor django-markup markdown +bleach +