Fix #306 -- Add HTML multipart version to emails

This commit is contained in:
Raphael Michel
2017-01-04 13:31:40 +01:00
parent 954af1de3d
commit 9d08e23a48
4 changed files with 206 additions and 9 deletions

View File

@@ -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

View File

@@ -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()

View File

@@ -0,0 +1,166 @@
{% load eventurl %}
{% load i18n %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=false">
</head>
<style type="text/css">
body {
background-color: #e8e8e8;
background-position: top;
background-repeat: repeat-x;
font-family: "Open Sans", "OpenSans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
color: #333;
margin: 0;
}
.header h1 {
margin-top: 20px;
margin-bottom: 5px;
}
.header h1 a {
text-decoration: none;
}
a {
color: {{ color }};
font-weight: bold;
}
a:hover, a:focus {
color: {{ color }};
text-decoration: underline;
}
a:hover, a:active {
outline: 0;
}
p {
margin: 0 0 10px;
/* These are technically the same, but use both */
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
/* This is the dangerous one in WebKit, as it breaks things wherever */
word-break: break-all;
/* Instead use this non-standard one: */
word-break: break-word;
/* Adds a hyphen where the word breaks, if supported (No Blink) */
-ms-hyphens: auto;
-moz-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
.footer {
padding: 10px;
text-align: center;
font-size: 12px;
}
.content {
padding: 8px 18px 8px;
}
::selection {
background: {{ color }};
color: #FFF;
}
table {
width: 90%;
max-width: 900px;
border-spacing: 0px;
border-collapse: separate;
margin: auto;
}
@media (max-width: 480px) {
.header h1 {
font-size: 19px;
line-height: 24px;
margin: 0 9px 3px 0;
border-radius: 5px 5px;
-webkit-border-radius: 5px 5px;
-moz-border-radius: 5px 5px;
}
.header h1 a {
padding: 3px 9px;
display: block;
}
.header {
margin: 0;
padding: 12px 0 8px;
}
}
td.containertd {
background-color: #FFFFFF;
border: 1px solid #cccccc;
}
{% block addcss %}{% endblock %}
</style>
<body>
<table>
<tr>
<td class="header" background="">
{% if event %}
<h1><a href="{% eventurl event "presale:event.index" %}" target="_blank">{{ event.name }}</a></h1>
{% else %}
<h1><a href="{{ site_url }}" target="_blank">{{ site }}</a></h1>
{% endif %}
</td>
</tr>
<tr>
<td class="containertd">
<div class="content">
{{ body|safe }}
</div>
</td>
</tr>
{% if order %}
<tr>
<td class="gap"></td>
</tr>
<tr>
<td class="order containertd">
<div class="content">
{% trans "You are receiving this email because you placed an order for the following event:" %}<br>
<strong>{% trans "Event:" %}</strong> {{ event.name }}<br>
<strong>{% trans "Order code:" %}</strong> {{ order.code }}<br>
<strong>{% trans "Order date:" %}</strong> {{ order.datetime|date:"SHORT_DATE_FORMAT" }}<br>
<a href="{% eventurl event "presale:event.order" order=order.code secret=order.secret %}">
{% trans "View order details" %}
</a>
</div>
</td>
</tr>
{% endif %}
<tr>
<td class="footer">
<div>
{% with 'target="blank" href="https://pretix.eu"'|safe as a_attr %}
{% blocktrans trimmed %}
powered by <a {{ a_attr }}>pretix</a>
{% endblocktrans %}
{% endwith %}
</div>
</td>
</tr>
</table>
<br/>
<br/>
</body>
</html>

View File

@@ -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