Allow to send e-mails to attendees individually (#1299)

* .

* Add a position detail page to the frontend

* Mail templates

* Send mails

* Send reminder email

* Add position support to sendmail plugin

* Add and fix some tests

* Fix failing test on real databases
This commit is contained in:
Raphael Michel
2019-05-24 09:41:44 +02:00
committed by GitHub
parent d22a7844ea
commit f1bce0c08b
29 changed files with 1078 additions and 213 deletions

View File

@@ -9,6 +9,12 @@ from pretix.control.forms.widgets import Select2
class MailForm(forms.Form):
recipients = forms.ChoiceField(
label=_('Send email to'),
widget=forms.RadioSelect,
initial='orders',
choices=[]
)
sendto = forms.MultipleChoiceField() # overridden later
subject = forms.CharField(label=_("Subject"))
message = forms.CharField(label=_("Message"))
@@ -30,6 +36,18 @@ class MailForm(forms.Form):
def __init__(self, *args, **kwargs):
event = kwargs.pop('event')
super().__init__(*args, **kwargs)
recp_choices = [
('orders', _('Everyone who created a ticket order'))
]
if event.settings.attendee_emails_asked:
recp_choices += [
('attendees', _('Every attendee (falling back to the order contact when no attendee email address is '
'given)')),
('both', _('Both (all order contact addresses and all attendee email addresses)'))
]
self.fields['recipients'].choices = recp_choices
self.fields['subject'] = I18nFormField(
label=_('Subject'),
widget=I18nTextInput, required=True,

View File

@@ -46,7 +46,8 @@ def control_nav_import(sender, request=None, **kwargs):
def pretixcontrol_logentry_display(sender, logentry, **kwargs):
plains = {
'pretix.plugins.sendmail.sent': _('Email was sent'),
'pretix.plugins.sendmail.order.email.sent': _('The order received a mass email.')
'pretix.plugins.sendmail.order.email.sent': _('The order received a mass email.'),
'pretix.plugins.sendmail.order.email.sent.attendee': _('A ticket holder of this order received a mass email.'),
}
if logentry.action_type in plains:
return plains[logentry.action_type]

View File

@@ -11,11 +11,11 @@ from pretix.multidomain.urlreverse import build_absolute_uri
@app.task(base=ProfiledTask)
def send_mails(event: int, user: int, subject: dict, message: dict, orders: list) -> None:
def send_mails(event: int, user: int, subject: dict, message: dict, orders: list, items: list, recipients: str) -> None:
failures = []
event = Event.objects.get(pk=event)
user = User.objects.get(pk=user) if user else None
orders = Order.objects.filter(pk__in=orders)
orders = Order.objects.filter(pk__in=orders, event=event)
subject = LazyI18nString(subject)
message = LazyI18nString(message)
tz = pytz.timezone(event.settings.timezone)
@@ -27,37 +27,95 @@ def send_mails(event: int, user: int, subject: dict, message: dict, orders: list
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
try:
with language(o.locale):
email_context = {
'event': o.event,
'code': o.code,
'date': date_format(o.datetime.astimezone(tz), 'SHORT_DATETIME_FORMAT'),
'expire_date': date_format(o.expires, 'SHORT_DATE_FORMAT'),
'url': build_absolute_uri(event, 'presale:event.order', kwargs={
'order': o.code,
'secret': o.secret
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
mail(
o.email,
subject,
message,
email_context,
event,
locale=o.locale,
order=o
)
o.log_action(
'pretix.plugins.sendmail.order.email.sent',
user=user,
data={
'subject': subject.localize(o.locale).format_map(email_context),
'message': message.localize(o.locale).format_map(email_context),
'recipient': o.email
send_to_order = recipients in ('both', 'orders')
if recipients in ('both', 'attendees'):
for p in o.positions.prefetch_related('addons'):
if p.addon_to_id is not None:
continue
if p.item_id not in items and not any(a.item_id in items for a in p.addons.all()):
continue
if not p.attendee_email:
if recipients == 'attendees':
send_to_order = True
continue
if p.attendee_email == o.email and send_to_order:
continue
try:
with language(o.locale):
email_context = {
'event': event,
'code': o.code,
'date': date_format(o.datetime.astimezone(tz), 'SHORT_DATETIME_FORMAT'),
'expire_date': date_format(o.expires, 'SHORT_DATE_FORMAT'),
'url': build_absolute_uri(event, 'presale:event.order.position', kwargs={
'order': o.code,
'secret': p.web_secret,
'position': p.positionid
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
mail(
p.attendee_email,
subject,
message,
email_context,
event,
locale=o.locale,
order=o,
position=p
)
o.log_action(
'pretix.plugins.sendmail.order.email.sent.attendee',
user=user,
data={
'position': p.positionid,
'subject': subject.localize(o.locale).format_map(email_context),
'message': message.localize(o.locale).format_map(email_context),
'recipient': p.attendee_email
}
)
except SendMailException:
failures.append(p.attendee_email)
if send_to_order and o.email:
try:
with language(o.locale):
email_context = {
'event': event,
'code': o.code,
'date': date_format(o.datetime.astimezone(tz), 'SHORT_DATETIME_FORMAT'),
'expire_date': date_format(o.expires, 'SHORT_DATE_FORMAT'),
'url': build_absolute_uri(event, 'presale:event.order.open', kwargs={
'order': o.code,
'secret': o.secret,
'hash': o.email_confirm_hash()
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
)
except SendMailException:
failures.append(o.email)
mail(
o.email,
subject,
message,
email_context,
event,
locale=o.locale,
order=o
)
o.log_action(
'pretix.plugins.sendmail.order.email.sent',
user=user,
data={
'subject': subject.localize(o.locale).format_map(email_context),
'message': message.localize(o.locale).format_map(email_context),
'recipient': o.email
}
)
except SendMailException:
failures.append(o.email)

View File

@@ -26,6 +26,13 @@
{% if log.pdata.subevent_obj %}
<br/><span class="fa fa-calendar fa-fw"></span> {{ log.pdata.subevent_obj }}
{% endif %}
{% if log.pdata.recipients == "attendees" %}
<br/><span class="fa fa-envelope fa-fw"></span> {% trans "Attendee contact addresses" %}
{% elif log.pdata.recipients == "both" %}
<br/><span class="fa fa-envelope fa-fw"></span> {% trans "All contact addresses" %}
{% else%}
<br/><span class="fa fa-envelope fa-fw"></span> {% trans "Order contact addresses" %}
{% endif %}
</p>
<p>
{% for locale, value in log.pdata.locales.items %}

View File

@@ -7,6 +7,7 @@
{% block inner %}
<form class="form-horizontal" method="post" action="">
{% csrf_token %}
{% bootstrap_field form.recipients layout='horizontal' %}
{% bootstrap_field form.sendto layout='horizontal' %}
{% if form.subevent %}
{% bootstrap_field form.subevent layout='horizontal' %}

View File

@@ -40,6 +40,7 @@ class SenderView(EventPermissionRequiredMixin, FormView):
action_type='pretix.plugins.sendmail.sent'
)
kwargs['initial'] = {
'recipients': logentry.parsed_data['recipients'],
'message': LazyI18nString(logentry.parsed_data['message']),
'subject': LazyI18nString(logentry.parsed_data['subject']),
'sendto': logentry.parsed_data['sendto'],
@@ -68,7 +69,7 @@ class SenderView(EventPermissionRequiredMixin, FormView):
return super().form_invalid(form)
def form_valid(self, form):
qs = Order.objects.filter(event=self.request.event, email__isnull=False)
qs = Order.objects.filter(event=self.request.event)
statusq = Q(status__in=form.cleaned_data['sendto'])
if 'overdue' in form.cleaned_data['sendto']:
statusq |= Q(status=Order.STATUS_PENDING, expires__lt=now())
@@ -118,17 +119,20 @@ class SenderView(EventPermissionRequiredMixin, FormView):
send_mails.apply_async(
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')]
}
)
self.request.event.log_action('pretix.plugins.sendmail.sent',
user=self.request.user,
data=dict(form.cleaned_data))
messages.success(self.request, _('Your message has been queued and will be sent to %d users in the next minutes.') % len(orders))
messages.success(self.request, _('Your message has been queued and will be sent to the contact addresses of %d '
'orders in the next minutes.') % len(orders))
return redirect(
'plugins:sendmail:send',