mirror of
https://github.com/pretix/pretix.git
synced 2026-01-02 18:42:27 +00:00
Fix #306 -- Add HTML multipart version to emails
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
166
src/pretix/base/templates/pretixbase/email/plainwrapper.html
Normal file
166
src/pretix/base/templates/pretixbase/email/plainwrapper.html
Normal 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>
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user