Sendmail plugin: Allow to attach a file to emails (#1814)

* sendmail: allow to attach files to emails

* Fix mixup of model objects and model IDs

* Attach to order-level emails, not only position-level emails

* Give attachments a proper file type

* Add a warning note about higher spam chances

Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
This commit is contained in:
Sohalt
2020-10-22 09:53:19 +02:00
committed by GitHub
parent d673a43130
commit 9e4dc344a4
5 changed files with 51 additions and 18 deletions

View File

@@ -231,7 +231,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
attach_tickets=attach_tickets, attach_tickets=attach_tickets,
attach_ical=attach_ical, attach_ical=attach_ical,
user=user.pk if user else None, user=user.pk if user else None,
attach_cached_files=[cf.id for cf in attach_cached_files] if attach_cached_files else [], attach_cached_files=[(cf.id if isinstance(cf, CachedFile) else cf) for cf in attach_cached_files] if attach_cached_files else [],
) )
if invoices: if invoices:

View File

@@ -7,6 +7,7 @@ from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput
from pretix.base.email import get_available_placeholders from pretix.base.email import get_available_placeholders
from pretix.base.forms import PlaceholderValidator from pretix.base.forms import PlaceholderValidator
from pretix.base.models import CheckinList, Item, Order, SubEvent from pretix.base.models import CheckinList, Item, Order, SubEvent
from pretix.control.forms import ExtFileField
from pretix.control.forms.widgets import Select2, Select2Multiple from pretix.control.forms.widgets import Select2, Select2Multiple
@@ -20,6 +21,18 @@ class MailForm(forms.Form):
sendto = forms.MultipleChoiceField() # overridden later sendto = forms.MultipleChoiceField() # overridden later
subject = forms.CharField(label=_("Subject")) subject = forms.CharField(label=_("Subject"))
message = forms.CharField(label=_("Message")) message = forms.CharField(label=_("Message"))
attachment = ExtFileField(
label=_("Attachment"),
required=False,
ext_whitelist=(
".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg",
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
".bmp", ".tif", ".tiff"
),
help_text=_('Sending an attachment increases the chance of your email not arriving or being sorted into spam folders. We recommend only using PDFs '
'of no more than 2 MB in size.'),
max_size=10 * 1024 * 1024
) # TODO i18n
items = forms.ModelMultipleChoiceField( items = forms.ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple( widget=forms.CheckboxSelectMultiple(
attrs={'class': 'scrolling-multiple-choice'} attrs={'class': 'scrolling-multiple-choice'}

View File

@@ -10,7 +10,8 @@ from pretix.celery_app import app
@app.task(base=ProfiledEventTask, acks_late=True) @app.task(base=ProfiledEventTask, acks_late=True)
def send_mails(event: Event, user: int, subject: dict, message: dict, orders: list, items: list, def send_mails(event: Event, user: int, subject: dict, message: dict, orders: list, items: list,
recipients: str, filter_checkins: bool, not_checked_in: bool, checkin_lists: list) -> None: recipients: str, filter_checkins: bool, not_checked_in: bool, checkin_lists: list,
attachments: list = None) -> None:
failures = [] failures = []
user = User.objects.get(pk=user) if user else None user = User.objects.get(pk=user) if user else None
orders = Order.objects.filter(pk__in=orders, event=event) orders = Order.objects.filter(pk__in=orders, event=event)
@@ -61,7 +62,8 @@ def send_mails(event: Event, user: int, subject: dict, message: dict, orders: li
event, event,
locale=o.locale, locale=o.locale,
order=o, order=o,
position=p position=p,
attach_cached_files=attachments
) )
o.log_action( o.log_action(
'pretix.plugins.sendmail.order.email.sent.attendee', 'pretix.plugins.sendmail.order.email.sent.attendee',
@@ -87,7 +89,8 @@ def send_mails(event: Event, user: int, subject: dict, message: dict, orders: li
email_context, email_context,
event, event,
locale=o.locale, locale=o.locale,
order=o order=o,
attach_cached_files=attachments
) )
o.log_action( o.log_action(
'pretix.plugins.sendmail.order.email.sent', 'pretix.plugins.sendmail.order.email.sent',

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<h1>{% trans "Send out emails" %}</h1> <h1>{% trans "Send out emails" %}</h1>
{% block inner %} {% block inner %}
<form class="form-horizontal" method="post" action=""> <form class="form-horizontal" method="post" action="" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{% bootstrap_field form.recipients layout='horizontal' %} {% bootstrap_field form.recipients layout='horizontal' %}
{% bootstrap_field form.sendto layout='horizontal' %} {% bootstrap_field form.sendto layout='horizontal' %}
@@ -32,6 +32,7 @@
</div> </div>
{% bootstrap_field form.subject layout='horizontal' %} {% bootstrap_field form.subject layout='horizontal' %}
{% bootstrap_field form.message layout='horizontal' %} {% bootstrap_field form.message layout='horizontal' %}
{% bootstrap_field form.attachment layout='horizontal' %}
{% if request.method == "POST" %} {% if request.method == "POST" %}
<fieldset> <fieldset>
<legend>{% trans "E-mail preview" %}</legend> <legend>{% trans "E-mail preview" %}</legend>

View File

@@ -1,4 +1,5 @@
import logging import logging
from datetime import timedelta
import bleach import bleach
from django.contrib import messages from django.contrib import messages
@@ -11,7 +12,7 @@ from django.views.generic import FormView, ListView
from pretix.base.email import get_available_placeholders from pretix.base.email import get_available_placeholders
from pretix.base.i18n import LazyI18nString, language from pretix.base.i18n import LazyI18nString, language
from pretix.base.models import LogEntry, Order, OrderPosition from pretix.base.models import CachedFile, LogEntry, Order, OrderPosition
from pretix.base.models.event import SubEvent from pretix.base.models.event import SubEvent
from pretix.base.services.mail import TolerantDict from pretix.base.services.mail import TolerantDict
from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.templatetags.rich_text import markdown_compile_email
@@ -136,19 +137,34 @@ class SenderView(EventPermissionRequiredMixin, FormView):
return self.get(self.request, *self.args, **self.kwargs) return self.get(self.request, *self.args, **self.kwargs)
attachment = None
if 'attachment' in self.request.FILES:
attachment = self.request.FILES['attachment']
cf = CachedFile.objects.create(
expires=now() + timedelta(days=1),
date=now(),
filename=attachment.name,
type=attachment.content_type,
)
cf.file.save(attachment.name, attachment.file)
cf.save()
kwargs = {
'recipients': form.cleaned_data['recipients'],
'event': self.request.event.pk,
'user': self.request.user.pk,
'subject': form.cleaned_data['subject'].data,
'message': form.cleaned_data['message'].data,
'orders': [o.pk for o in orders],
'items': [i.pk for i in form.cleaned_data.get('items')],
'not_checked_in': form.cleaned_data.get('not_checked_in'),
'checkin_lists': [i.pk for i in form.cleaned_data.get('checkin_lists')],
'filter_checkins': form.cleaned_data.get('filter_checkins'),
}
if attachment is not None:
kwargs['attachments'] = [cf.id]
send_mails.apply_async( send_mails.apply_async(
kwargs={ kwargs=kwargs
'recipients': form.cleaned_data['recipients'],
'event': self.request.event.pk,
'user': self.request.user.pk,
'subject': form.cleaned_data['subject'].data,
'message': form.cleaned_data['message'].data,
'orders': [o.pk for o in orders],
'items': [i.pk for i in form.cleaned_data.get('items')],
'not_checked_in': form.cleaned_data.get('not_checked_in'),
'checkin_lists': [i.pk for i in form.cleaned_data.get('checkin_lists')],
'filter_checkins': form.cleaned_data.get('filter_checkins'),
}
) )
self.request.event.log_action('pretix.plugins.sendmail.sent', self.request.event.log_action('pretix.plugins.sendmail.sent',
user=self.request.user, user=self.request.user,