Fix #467 -- Pluggable email placeholders (#1429)

* Fix #467 -- Pluggable email placeholders

* Previews

* Polishing

* Fix tests

* Add missing doc file
This commit is contained in:
Raphael Michel
2019-10-07 11:48:25 +02:00
committed by GitHub
parent cb37e7435d
commit 1d0c148170
26 changed files with 572 additions and 548 deletions

View File

@@ -2,25 +2,21 @@ import logging
import re
from decimal import Decimal
import pytz
from celery.exceptions import MaxRetriesExceededError
from django.conf import settings
from django.db import transaction
from django.db.models import Q
from django.utils.formats import date_format
from django.utils.translation import ugettext, ugettext_noop
from django_scopes import scope, scopes_disabled
from pretix.base.email import get_email_context
from pretix.base.i18n import language
from pretix.base.models import (
Event, InvoiceAddress, Order, OrderPayment, Organizer, Quota,
)
from pretix.base.models import Event, Order, OrderPayment, Organizer, Quota
from pretix.base.services.locking import LockTimeoutException
from pretix.base.services.mail import SendMailException
from pretix.base.services.orders import change_payment_provider
from pretix.base.services.tasks import TransactionAwareTask
from pretix.celery_app import app
from pretix.multidomain.urlreverse import build_absolute_uri
from .models import BankImportJob, BankTransaction
@@ -29,25 +25,8 @@ logger = logging.getLogger(__name__)
def notify_incomplete_payment(o: Order):
with language(o.locale):
tz = pytz.timezone(o.event.settings.get('timezone', settings.TIME_ZONE))
try:
invoice_name = o.invoice_address.name
invoice_company = o.invoice_address.company
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
email_template = o.event.settings.mail_text_order_expire_warning
email_context = {
'event': o.event.name,
'url': build_absolute_uri(o.event, 'presale:event.order.open', kwargs={
'order': o.code,
'secret': o.secret,
'hash': o.email_confirm_hash()
}),
'expire_date': date_format(o.expires.astimezone(tz), 'SHORT_DATE_FORMAT'),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
email_context = get_email_context(event=o.event, order=o)
email_subject = ugettext('Your order received an incomplete payment: %(code)s') % {'code': o.code}
try:

View File

@@ -3,6 +3,7 @@ from django.urls import reverse
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput
from pretix.base.email import get_available_placeholders
from pretix.base.forms import PlaceholderValidator
from pretix.base.models import Item, Order, SubEvent
from pretix.control.forms.widgets import Select2
@@ -33,8 +34,24 @@ class MailForm(forms.Form):
empty_label=pgettext_lazy('subevent', 'All dates')
)
def _set_field_placeholders(self, fn, base_parameters):
phs = [
'{%s}' % p
for p in sorted(get_available_placeholders(self.event, base_parameters).keys())
]
ht = _('Available placeholders: {list}').format(
list=', '.join(phs)
)
if self.fields[fn].help_text:
self.fields[fn].help_text += ' ' + str(ht)
else:
self.fields[fn].help_text = ht
self.fields[fn].validators.append(
PlaceholderValidator(phs)
)
def __init__(self, *args, **kwargs):
event = kwargs.pop('event')
event = self.event = kwargs.pop('event')
super().__init__(*args, **kwargs)
recp_choices = [
@@ -52,20 +69,14 @@ class MailForm(forms.Form):
label=_('Subject'),
widget=I18nTextInput, required=True,
locales=event.settings.get('locales'),
help_text=_("Available placeholders: {expire_date}, {event}, {code}, {date}, {url}, "
"{invoice_name}, {invoice_company}"),
validators=[PlaceholderValidator(['{expire_date}', '{event}', '{code}', '{date}', '{url}',
'{invoice_name}', '{invoice_company}'])]
)
self.fields['message'] = I18nFormField(
label=_('Message'),
widget=I18nTextarea, required=True,
locales=event.settings.get('locales'),
help_text=_("Available placeholders: {expire_date}, {event}, {code}, {date}, {url}, "
"{invoice_name}, {invoice_company}"),
validators=[PlaceholderValidator(['{expire_date}', '{event}', '{code}', '{date}', '{url}',
'{invoice_name}', '{invoice_company}'])]
)
self._set_field_placeholders('subject', ['event', 'order', 'position_or_address'])
self._set_field_placeholders('message', ['event', 'order', 'position_or_address'])
choices = list(Order.STATUS_CHOICE)
if not event.settings.get('payment_term_expire_automatically', as_type=bool):
choices.append(

View File

@@ -1,13 +1,11 @@
import pytz
from django.utils.formats import date_format
from i18nfield.strings import LazyI18nString
from pretix.base.email import get_email_context
from pretix.base.i18n import language
from pretix.base.models import Event, InvoiceAddress, Order, User
from pretix.base.services.mail import SendMailException, mail
from pretix.base.services.tasks import ProfiledEventTask
from pretix.celery_app import app
from pretix.multidomain.urlreverse import build_absolute_uri
@app.task(base=ProfiledEventTask)
@@ -17,17 +15,15 @@ def send_mails(event: Event, user: int, subject: dict, message: dict, orders: li
orders = Order.objects.filter(pk__in=orders, event=event)
subject = LazyI18nString(subject)
message = LazyI18nString(message)
tz = pytz.timezone(event.settings.timezone)
for o in orders:
try:
invoice_name = o.invoice_address.name
invoice_company = o.invoice_address.company
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
send_to_order = recipients in ('both', 'orders')
try:
ia = o.invoice_address
except InvoiceAddress.DoesNotExist:
ia = InvoiceAddress()
if recipients in ('both', 'attendees'):
for p in o.positions.prefetch_related('addons'):
if p.addon_to_id is not None:
@@ -46,19 +42,7 @@ def send_mails(event: Event, user: int, subject: dict, message: dict, orders: li
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,
}
email_context = get_email_context(event=event, order=o, position_or_address=p, position=p)
mail(
p.attendee_email,
subject,
@@ -85,19 +69,7 @@ def send_mails(event: Event, user: int, subject: dict, message: dict, orders: li
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,
}
email_context = get_email_context(event=event, order=o, position_or_address=ia)
mail(
o.email,
subject,

View File

@@ -21,7 +21,7 @@
<div class="tab-pane mail-preview-group">
{% for locale, out in output.items %}
<div lang="{{ locale }}" class="mail-preview">
<strong>{{ out.subject }}</strong><br><br>
<strong>{{ out.subject|safe }}</strong><br><br>
{{ out.html|safe }}
</div>
{% endfor %}

View File

@@ -1,21 +1,21 @@
import logging
from datetime import timedelta
import bleach
from django.contrib import messages
from django.db.models import Q
from django.http import Http404
from django.shortcuts import redirect
from django.utils.formats import date_format
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView, ListView
from pretix.base.email import get_available_placeholders
from pretix.base.i18n import LazyI18nString, language
from pretix.base.models import LogEntry, Order
from pretix.base.models.event import SubEvent
from pretix.base.services.mail import TolerantDict
from pretix.base.templatetags.rich_text import markdown_compile_email
from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.multidomain.urlreverse import build_absolute_uri
from pretix.plugins.sendmail.tasks import send_mails
from . import forms
@@ -90,22 +90,15 @@ class SenderView(EventPermissionRequiredMixin, FormView):
for l in self.request.event.settings.locales:
with language(l):
context_dict = TolerantDict()
for k, v in get_available_placeholders(self.request.event, ['event', 'order',
'position_or_address']).items():
context_dict[k] = '<span class="placeholder" title="{}">{}</span>'.format(
_('This value will be replaced based on dynamic parameters.'),
v.render_sample(self.request.event)
)
context_dict = {
'code': 'ORDER1234',
'event': self.request.event.name,
'date': date_format(now(), 'SHORT_DATE_FORMAT'),
'expire_date': date_format(now() + timedelta(days=7), 'SHORT_DATE_FORMAT'),
'url': build_absolute_uri(self.request.event, 'presale:event.order.open', kwargs={
'order': 'ORDER1234',
'secret': 'longrandomsecretabcdef123456',
'hash': 'abcdef',
}),
'invoice_name': _('John Doe'),
'invoice_company': _('Sample Company LLC')
}
subject = form.cleaned_data['subject'].localize(l)
subject = bleach.clean(form.cleaned_data['subject'].localize(l), tags=[])
preview_subject = subject.format_map(context_dict)
message = form.cleaned_data['message'].localize(l)
preview_text = markdown_compile_email(message.format_map(context_dict))