Add option to attach calendar files to emails (#1457)

This commit is contained in:
Raphael Michel
2019-10-21 10:41:22 +02:00
committed by GitHub
parent 2b18621c76
commit 19b10e3ca4
6 changed files with 43 additions and 9 deletions
+8 -5
View File
@@ -723,7 +723,8 @@ class Order(LockModel, LoggedModel):
def send_mail(self, subject: str, template: Union[str, LazyI18nString], def send_mail(self, subject: str, template: Union[str, LazyI18nString],
context: Dict[str, Any]=None, log_entry_type: str='pretix.event.order.email.sent', context: Dict[str, Any]=None, log_entry_type: str='pretix.event.order.email.sent',
user: User=None, headers: dict=None, sender: str=None, invoices: list=None, user: User=None, headers: dict=None, sender: str=None, invoices: list=None,
auth=None, attach_tickets=False, position: 'OrderPosition'=None, auto_email=True): auth=None, attach_tickets=False, position: 'OrderPosition'=None, auto_email=True,
attach_ical=False):
""" """
Sends an email to the user that placed this order. Basically, this method does two things: Sends an email to the user that placed this order. Basically, this method does two things:
@@ -740,6 +741,7 @@ class Order(LockModel, LoggedModel):
:param headers: Dictionary with additional mail headers :param headers: Dictionary with additional mail headers
:param sender: Custom email sender. :param sender: Custom email sender.
:param attach_tickets: Attach tickets of this order, if they are existing and ready to download :param attach_tickets: Attach tickets of this order, if they are existing and ready to download
:param attach_ical: Attach relevant ICS files
:param position: An order position this refers to. If given, no invoices will be attached, the tickets will :param position: An order position this refers to. If given, no invoices will be attached, the tickets will
only be attached for this position and child positions, the link will only point to the only be attached for this position and child positions, the link will only point to the
position and the attendee email will be used if available. position and the attendee email will be used if available.
@@ -763,7 +765,7 @@ class Order(LockModel, LoggedModel):
recipient, subject, template, context, recipient, subject, template, context,
self.event, self.locale, self, headers=headers, sender=sender, self.event, self.locale, self, headers=headers, sender=sender,
invoices=invoices, attach_tickets=attach_tickets, invoices=invoices, attach_tickets=attach_tickets,
position=position, auto_email=auto_email position=position, auto_email=auto_email, attach_ical=attach_ical
) )
except SendMailException: except SendMailException:
raise raise
@@ -779,6 +781,7 @@ class Order(LockModel, LoggedModel):
'recipient': recipient, 'recipient': recipient,
'invoices': [i.pk for i in invoices] if invoices else [], 'invoices': [i.pk for i in invoices] if invoices else [],
'attach_tickets': attach_tickets, 'attach_tickets': attach_tickets,
'attach_ical': attach_ical,
} }
) )
@@ -790,7 +793,7 @@ class Order(LockModel, LoggedModel):
self.send_mail( self.send_mail(
email_subject, email_template, email_context, email_subject, email_template, email_context,
'pretix.event.order.email.resend', user=user, auth=auth, 'pretix.event.order.email.resend', user=user, auth=auth,
attach_tickets=True attach_tickets=True,
) )
@property @property
@@ -1334,7 +1337,7 @@ class OrderPayment(models.Model):
email_subject, email_template, email_context, email_subject, email_template, email_context,
'pretix.event.order.email.order_paid', user, 'pretix.event.order.email.order_paid', user,
invoices=[], position=position, invoices=[], position=position,
attach_tickets=True attach_tickets=True, attach_ical=True
) )
except SendMailException: except SendMailException:
logger.exception('Order paid email could not be sent') logger.exception('Order paid email could not be sent')
@@ -1351,7 +1354,7 @@ class OrderPayment(models.Model):
email_subject, email_template, email_context, email_subject, email_template, email_context,
'pretix.event.order.email.order_paid', user, 'pretix.event.order.email.order_paid', user,
invoices=[invoice] if invoice and self.order.event.settings.invoice_email_attachment else [], invoices=[invoice] if invoice and self.order.event.settings.invoice_email_attachment else [],
attach_tickets=True attach_tickets=True, attach_ical=True
) )
except SendMailException: except SendMailException:
logger.exception('Order paid email could not be sent') logger.exception('Order paid email could not be sent')
+21 -2
View File
@@ -31,6 +31,7 @@ from pretix.base.services.tickets import get_tickets_for_order
from pretix.base.signals import email_filter, global_email_filter from pretix.base.signals import email_filter, global_email_filter
from pretix.celery_app import app from pretix.celery_app import app
from pretix.multidomain.urlreverse import build_absolute_uri from pretix.multidomain.urlreverse import build_absolute_uri
from pretix.presale.ical import get_ical
logger = logging.getLogger('pretix.base.mail') logger = logging.getLogger('pretix.base.mail')
INVALID_ADDRESS = 'invalid-pretix-mail-address' INVALID_ADDRESS = 'invalid-pretix-mail-address'
@@ -50,7 +51,7 @@ class SendMailException(Exception):
def mail(email: str, subject: str, template: Union[str, LazyI18nString], def mail(email: str, subject: str, template: Union[str, LazyI18nString],
context: Dict[str, Any]=None, event: Event=None, locale: str=None, context: Dict[str, Any]=None, event: Event=None, locale: str=None,
order: Order=None, position: OrderPosition=None, headers: dict=None, sender: str=None, order: Order=None, position: OrderPosition=None, headers: dict=None, sender: str=None,
invoices: list=None, attach_tickets=False, auto_email=True, user=None): invoices: list=None, attach_tickets=False, auto_email=True, user=None, attach_ical=False):
""" """
Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation. Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation.
@@ -85,6 +86,8 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
:param attach_tickets: Whether to attach tickets to this email, if they are available to download. :param attach_tickets: Whether to attach tickets to this email, if they are available to download.
:param attach_ical: Whether to attach relevant ``.ics`` files to this email
:param auto_email: Whether this email is auto-generated :param auto_email: Whether this email is auto-generated
:param user: The user this email is sent to :param user: The user this email is sent to
@@ -216,6 +219,7 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
order=order.pk if order else None, order=order.pk if order else None,
position=position.pk if position else None, position=position.pk if position else None,
attach_tickets=attach_tickets, attach_tickets=attach_tickets,
attach_ical=attach_ical,
user=user.pk if user else None user=user.pk if user else None
) )
@@ -231,7 +235,8 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
@app.task(base=TransactionAwareTask, bind=True) @app.task(base=TransactionAwareTask, bind=True)
def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: str, sender: str, def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: str, sender: str,
event: int=None, position: int=None, headers: dict=None, bcc: List[str]=None, event: int=None, position: int=None, headers: dict=None, bcc: List[str]=None,
invoices: List[int]=None, order: int=None, attach_tickets=False, user=None) -> bool: invoices: List[int]=None, order: int=None, attach_tickets=False, user=None,
attach_ical=False) -> bool:
email = EmailMultiAlternatives(subject, body, sender, to=to, bcc=bcc, headers=headers) email = EmailMultiAlternatives(subject, body, sender, to=to, bcc=bcc, headers=headers)
if html is not None: if html is not None:
html_with_cid, cid_images = replace_images_with_cid_paths(html) html_with_cid, cid_images = replace_images_with_cid_paths(html)
@@ -301,6 +306,20 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
'invoices': [], 'invoices': [],
} }
) )
if attach_ical:
ical_events = set()
if event.has_subevents:
if position:
ical_events.add(position.subevent)
else:
for p in order.positions.all():
ical_events.add(p.subevent)
else:
ical_events.add(order.event)
for i, e in enumerate(ical_events):
cal = get_ical([e])
email.attach('event-{}.ics'.format(i), cal.serialize(), 'text/calendar')
email = email_filter.send_chained(event, 'message', message=email, order=order, user=user) email = email_filter.send_chained(event, 'message', message=email, order=order, user=user)
+4 -2
View File
@@ -707,7 +707,8 @@ def _order_placed_email(event: Event, order: Order, pprov: BasePaymentProvider,
email_subject, email_template, email_context, email_subject, email_template, email_context,
log_entry, log_entry,
invoices=[invoice] if invoice and event.settings.invoice_email_attachment else [], invoices=[invoice] if invoice and event.settings.invoice_email_attachment else [],
attach_tickets=True attach_tickets=True,
attach_ical=event.settings.mail_attach_ical
) )
except SendMailException: except SendMailException:
logger.exception('Order received email could not be sent') logger.exception('Order received email could not be sent')
@@ -723,7 +724,8 @@ def _order_placed_email_attendee(event: Event, order: Order, position: OrderPosi
log_entry, log_entry,
invoices=[], invoices=[],
attach_tickets=True, attach_tickets=True,
position=position position=position,
attach_ical=event.settings.mail_attach_ical
) )
except SendMailException: except SendMailException:
logger.exception('Order received email could not be sent to attendee') logger.exception('Order received email could not be sent to attendee')
+4
View File
@@ -297,6 +297,10 @@ DEFAULTS = {
'default': 'classic', 'default': 'classic',
'type': str 'type': str
}, },
'mail_attach_ical': {
'default': 'False',
'type': bool
},
'mail_prefix': { 'mail_prefix': {
'default': None, 'default': None,
'type': str 'type': str
+5
View File
@@ -976,6 +976,11 @@ class MailSettingsForm(SettingsForm):
required=False, required=False,
max_length=255 max_length=255
) )
mail_attach_ical = forms.BooleanField(
label=_("Attach calendar files"),
help_text=_("If enabled, we will attach an .ics calendar file to order confirmation emails."),
required=False
)
mail_text_signature = I18nFormField( mail_text_signature = I18nFormField(
label=_("Signature"), label=_("Signature"),
@@ -16,6 +16,7 @@
{% bootstrap_field form.mail_from_name layout="control" %} {% bootstrap_field form.mail_from_name layout="control" %}
{% bootstrap_field form.mail_text_signature layout="control" %} {% bootstrap_field form.mail_text_signature layout="control" %}
{% bootstrap_field form.mail_bcc layout="control" %} {% bootstrap_field form.mail_bcc layout="control" %}
{% bootstrap_field form.mail_attach_ical layout="control" %}
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>{% trans "E-mail design" %}</legend> <legend>{% trans "E-mail design" %}</legend>