mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
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:
@@ -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,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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' %}
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user