forked from CGM_Public/pretix_original
Catch and display mail sending errors (#215)
This commit is contained in:
committed by
Raphael Michel
parent
fe4946d591
commit
3c8f9f5a62
@@ -19,6 +19,10 @@ class TolerantDict(dict):
|
|||||||
return key
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
class SendMailException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def mail(email: str, subject: str, template: str,
|
def mail(email: str, subject: str, template: str,
|
||||||
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, headers: dict=None):
|
order: Order=None, headers: dict=None):
|
||||||
@@ -46,8 +50,8 @@ def mail(email: str, subject: str, template: str,
|
|||||||
|
|
||||||
:param locale: The locale to be used while evaluating the subject and the template
|
:param locale: The locale to be used while evaluating the subject and the template
|
||||||
|
|
||||||
:raises Exception: on obvious, immediate failures. Not raising an exception does not necessarily mean that the
|
:raises MailOrderException: on obvious, immediate failures. Not raising an exception does not necessarily mean
|
||||||
email has been sent, just that it has been queued by the email backend.
|
that the email has been sent, just that it has been queued by the email backend.
|
||||||
"""
|
"""
|
||||||
with language(locale):
|
with language(locale):
|
||||||
if isinstance(template, LazyI18nString):
|
if isinstance(template, LazyI18nString):
|
||||||
@@ -94,7 +98,7 @@ def mail_send(to: str, subject: str, body: str, sender: str, event: int=None, he
|
|||||||
backend.send_messages([email])
|
backend.send_messages([email])
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('Error sending email')
|
logger.exception('Error sending email')
|
||||||
raise
|
raise SendMailException('Failed to send an email to {}.'.format(to))
|
||||||
|
|
||||||
|
|
||||||
if settings.HAS_CELERY and settings.EMAIL_BACKEND != 'django.core.mail.outbox':
|
if settings.HAS_CELERY and settings.EMAIL_BACKEND != 'django.core.mail.outbox':
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from pretix.base.forms.auth import (
|
|||||||
LoginForm, PasswordForgotForm, PasswordRecoverForm, RegistrationForm,
|
LoginForm, PasswordForgotForm, PasswordRecoverForm, RegistrationForm,
|
||||||
)
|
)
|
||||||
from pretix.base.models import User
|
from pretix.base.models import User
|
||||||
from pretix.base.services.mail import mail
|
from pretix.base.services.mail import SendMailException, mail
|
||||||
from pretix.helpers.urls import build_absolute_uri
|
from pretix.helpers.urls import build_absolute_uri
|
||||||
|
|
||||||
|
|
||||||
@@ -103,15 +103,20 @@ class Forgot(TemplateView):
|
|||||||
else:
|
else:
|
||||||
rc.setex('pretix_pwreset_%s' % (user.id), 3600 * 24, '1')
|
rc.setex('pretix_pwreset_%s' % (user.id), 3600 * 24, '1')
|
||||||
|
|
||||||
mail(
|
try:
|
||||||
user.email, _('Password recovery'), 'pretixcontrol/email/forgot.txt',
|
mail(
|
||||||
{
|
user.email, _('Password recovery'), 'pretixcontrol/email/forgot.txt',
|
||||||
'user': user,
|
{
|
||||||
'url': (build_absolute_uri('control:auth.forgot.recover')
|
'user': user,
|
||||||
+ '?id=%d&token=%s' % (user.id, default_token_generator.make_token(user)))
|
'url': (build_absolute_uri('control:auth.forgot.recover')
|
||||||
},
|
+ '?id=%d&token=%s' % (user.id, default_token_generator.make_token(user)))
|
||||||
None, locale=user.locale
|
},
|
||||||
)
|
None, locale=user.locale
|
||||||
|
)
|
||||||
|
except SendMailException:
|
||||||
|
messages.error(request, _('There was an error sending the mail. Please try again later.'))
|
||||||
|
return self.get(request, *args, **kwargs)
|
||||||
|
|
||||||
user.log_action('pretix.control.auth.user.forgot_password.mail_sent')
|
user.log_action('pretix.control.auth.user.forgot_password.mail_sent')
|
||||||
messages.success(request, _('We sent you an e-mail containing further instructions.'))
|
messages.success(request, _('We sent you an e-mail containing further instructions.'))
|
||||||
return redirect('control:auth.forgot')
|
return redirect('control:auth.forgot')
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from pretix.base.services.invoices import (
|
|||||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_qualified,
|
generate_cancellation, generate_invoice, invoice_pdf, invoice_qualified,
|
||||||
regenerate_invoice,
|
regenerate_invoice,
|
||||||
)
|
)
|
||||||
from pretix.base.services.mail import mail
|
from pretix.base.services.mail import SendMailException, mail
|
||||||
from pretix.base.services.orders import cancel_order, mark_order_paid
|
from pretix.base.services.orders import cancel_order, mark_order_paid
|
||||||
from pretix.base.services.stats import order_overview
|
from pretix.base.services.stats import order_overview
|
||||||
from pretix.base.signals import (
|
from pretix.base.signals import (
|
||||||
@@ -201,6 +201,8 @@ class OrderTransition(OrderView):
|
|||||||
mark_order_paid(self.order, manual=True, user=self.request.user)
|
mark_order_paid(self.order, manual=True, user=self.request.user)
|
||||||
except Quota.QuotaExceededException as e:
|
except Quota.QuotaExceededException as e:
|
||||||
messages.error(self.request, str(e))
|
messages.error(self.request, str(e))
|
||||||
|
except SendMailException:
|
||||||
|
messages.warning(self.request, _('The order has been marked as paid, but we were unable to send a confirmation mail.'))
|
||||||
else:
|
else:
|
||||||
messages.success(self.request, _('The order has been marked as paid.'))
|
messages.success(self.request, _('The order has been marked as paid.'))
|
||||||
elif self.order.status == Order.STATUS_PENDING and to == 'c':
|
elif self.order.status == Order.STATUS_PENDING and to == 'c':
|
||||||
@@ -311,18 +313,23 @@ class OrderResendLink(OrderView):
|
|||||||
|
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
with language(self.order.locale):
|
with language(self.order.locale):
|
||||||
mail(
|
try:
|
||||||
self.order.email, _('Your order: %(code)s') % {'code': self.order.code},
|
mail(
|
||||||
self.order.event.settings.mail_text_resend_link,
|
self.order.email, _('Your order: %(code)s') % {'code': self.order.code},
|
||||||
{
|
self.order.event.settings.mail_text_resend_link,
|
||||||
'event': self.order.event.name,
|
{
|
||||||
'url': build_absolute_uri(self.order.event, 'presale:event.order', kwargs={
|
'event': self.order.event.name,
|
||||||
'order': self.order.code,
|
'url': build_absolute_uri(self.order.event, 'presale:event.order', kwargs={
|
||||||
'secret': self.order.secret
|
'order': self.order.code,
|
||||||
}),
|
'secret': self.order.secret
|
||||||
},
|
}),
|
||||||
self.order.event, locale=self.order.locale
|
},
|
||||||
)
|
self.order.event, locale=self.order.locale
|
||||||
|
)
|
||||||
|
except SendMailException:
|
||||||
|
messages.error(self.request, _('There was an error sending the mail. Please try again later.'))
|
||||||
|
return redirect(self.get_order_url())
|
||||||
|
|
||||||
messages.success(self.request, _('The email has been queued to be sent.'))
|
messages.success(self.request, _('The email has been queued to be sent.'))
|
||||||
self.order.log_action('pretix.event.order.resend', user=self.request.user)
|
self.order.log_action('pretix.event.order.resend', user=self.request.user)
|
||||||
return redirect(self.get_order_url())
|
return redirect(self.get_order_url())
|
||||||
|
|||||||
@@ -4276,8 +4276,9 @@ msgid "Send"
|
|||||||
msgstr "Senden"
|
msgstr "Senden"
|
||||||
|
|
||||||
#: pretix/plugins/sendmail/views.py:43
|
#: pretix/plugins/sendmail/views.py:43
|
||||||
msgid "Your message will be sent to the selected users."
|
msgid "Your message has been queued to be sent to the selected users."
|
||||||
msgstr "Die Nachricht wird an die ausgewählten Benutzer verschickt."
|
msgstr ""
|
||||||
|
"Die Nachricht wurde zum Versenden an die ausgewählten Benutzer gespeichert."
|
||||||
|
|
||||||
#: pretix/plugins/statistics/__init__.py:10
|
#: pretix/plugins/statistics/__init__.py:10
|
||||||
#: pretix/plugins/statistics/__init__.py:14
|
#: pretix/plugins/statistics/__init__.py:14
|
||||||
|
|||||||
@@ -4265,8 +4265,9 @@ msgid "Send"
|
|||||||
msgstr "Senden"
|
msgstr "Senden"
|
||||||
|
|
||||||
#: pretix/plugins/sendmail/views.py:43
|
#: pretix/plugins/sendmail/views.py:43
|
||||||
msgid "Your message will be sent to the selected users."
|
msgid "Your message has been queued to be sent to the selected users."
|
||||||
msgstr "Die Nachricht wird an die ausgewählten Benutzer verschickt."
|
msgstr ""
|
||||||
|
"Die Nachricht wurde zum Versenden an die ausgewählten Benutzer gespeichert."
|
||||||
|
|
||||||
#: pretix/plugins/statistics/__init__.py:10
|
#: pretix/plugins/statistics/__init__.py:10
|
||||||
#: pretix/plugins/statistics/__init__.py:14
|
#: pretix/plugins/statistics/__init__.py:14
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
from pretix.base.models import Order, Quota
|
from pretix.base.models import Order, Quota
|
||||||
|
from pretix.base.services.mail import SendMailException
|
||||||
from pretix.base.services.orders import mark_order_paid
|
from pretix.base.services.orders import mark_order_paid
|
||||||
from pretix.base.settings import SettingsSandbox
|
from pretix.base.settings import SettingsSandbox
|
||||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||||
@@ -54,6 +55,7 @@ class ImportView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
orders = Order.objects.filter(event=self.request.event,
|
orders = Order.objects.filter(event=self.request.event,
|
||||||
code__in=self.request.POST.getlist('mark_paid'))
|
code__in=self.request.POST.getlist('mark_paid'))
|
||||||
some_failed = False
|
some_failed = False
|
||||||
|
mail_failures = False
|
||||||
for order in orders:
|
for order in orders:
|
||||||
try:
|
try:
|
||||||
mark_order_paid(order, provider='banktransfer', info=json.dumps({
|
mark_order_paid(order, provider='banktransfer', info=json.dumps({
|
||||||
@@ -64,6 +66,8 @@ class ImportView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
}))
|
}))
|
||||||
except Quota.QuotaExceededException:
|
except Quota.QuotaExceededException:
|
||||||
some_failed = True
|
some_failed = True
|
||||||
|
except SendMailException:
|
||||||
|
mail_failures = True
|
||||||
|
|
||||||
if some_failed:
|
if some_failed:
|
||||||
messages.warning(self.request, _('Not all of the selected orders could be marked as '
|
messages.warning(self.request, _('Not all of the selected orders could be marked as '
|
||||||
@@ -72,6 +76,8 @@ class ImportView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
else:
|
else:
|
||||||
messages.success(self.request, _('The selected orders have been marked as paid.'))
|
messages.success(self.request, _('The selected orders have been marked as paid.'))
|
||||||
# TODO: Display a list of them!
|
# TODO: Display a list of them!
|
||||||
|
if mail_failures:
|
||||||
|
messages.warning(self.request, _('Some confirmation mails could not be sent.'))
|
||||||
return self.redirect_back()
|
return self.redirect_back()
|
||||||
|
|
||||||
messages.error(self.request, _('We were unable to detect the file type of this import. Please '
|
messages.error(self.request, _('We were unable to detect the file type of this import. Please '
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from django.utils.translation import ugettext as __, ugettext_lazy as _
|
|||||||
|
|
||||||
from pretix.base.models import Quota
|
from pretix.base.models import Quota
|
||||||
from pretix.base.payment import BasePaymentProvider
|
from pretix.base.payment import BasePaymentProvider
|
||||||
|
from pretix.base.services.mail import SendMailException
|
||||||
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
|
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||||
|
|
||||||
@@ -182,6 +183,8 @@ class Paypal(BasePaymentProvider):
|
|||||||
mark_order_paid(order, 'paypal', json.dumps(payment.to_dict()))
|
mark_order_paid(order, 'paypal', json.dumps(payment.to_dict()))
|
||||||
except Quota.QuotaExceededException as e:
|
except Quota.QuotaExceededException as e:
|
||||||
messages.error(request, str(e))
|
messages.error(request, str(e))
|
||||||
|
except SendMailException:
|
||||||
|
messages.warning(request, _('There was an error sending the confirmation mail.'))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def order_pending_render(self, request, order) -> str:
|
def order_pending_render(self, request, order) -> str:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.views.generic import FormView
|
from django.views.generic import FormView
|
||||||
|
|
||||||
from pretix.base.models import Order
|
from pretix.base.models import Order
|
||||||
from pretix.base.services.mail import mail
|
from pretix.base.services.mail import SendMailException, mail
|
||||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||||
|
|
||||||
from . import forms
|
from . import forms
|
||||||
@@ -36,11 +36,18 @@ class SenderView(EventPermissionRequiredMixin, FormView):
|
|||||||
self.request.event.log_action('pretix.plugins.sendmail.sent', user=self.request.user, data=dict(
|
self.request.event.log_action('pretix.plugins.sendmail.sent', user=self.request.user, data=dict(
|
||||||
form.cleaned_data))
|
form.cleaned_data))
|
||||||
|
|
||||||
|
failures = []
|
||||||
for o in orders:
|
for o in orders:
|
||||||
mail(o.email, form.cleaned_data['subject'], form.cleaned_data['message'],
|
try:
|
||||||
None, self.request.event, locale=o.locale, order=o)
|
mail(o.email, form.cleaned_data['subject'], form.cleaned_data['message'],
|
||||||
|
None, self.request.event, locale=o.locale, order=o)
|
||||||
|
except SendMailException:
|
||||||
|
failures.append(o.email)
|
||||||
|
|
||||||
messages.success(self.request, _('Your message will be sent to the selected users.'))
|
if failures:
|
||||||
|
messages.error(self.request, _('Failed to send mails to the following users: {}'.format(' '.join(failures))))
|
||||||
|
else:
|
||||||
|
messages.success(self.request, _('Your message has been queued to be sent to the selected users.'))
|
||||||
|
|
||||||
return redirect(
|
return redirect(
|
||||||
'plugins:sendmail:send',
|
'plugins:sendmail:send',
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from pretix.base.models import Quota
|
from pretix.base.models import Quota
|
||||||
from pretix.base.payment import BasePaymentProvider
|
from pretix.base.payment import BasePaymentProvider
|
||||||
|
from pretix.base.services.mail import SendMailException
|
||||||
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
|
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||||
|
|
||||||
@@ -116,6 +117,9 @@ class Stripe(BasePaymentProvider):
|
|||||||
mark_order_paid(order, 'stripe', str(charge))
|
mark_order_paid(order, 'stripe', str(charge))
|
||||||
except Quota.QuotaExceededException as e:
|
except Quota.QuotaExceededException as e:
|
||||||
messages.error(request, str(e))
|
messages.error(request, str(e))
|
||||||
|
except SendMailException:
|
||||||
|
messages.warning(request, _('There was an error sending the confirmation mail.'))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
messages.warning(request, _('Stripe reported an error: %s' % charge.failure_message))
|
messages.warning(request, _('Stripe reported an error: %s' % charge.failure_message))
|
||||||
logger.info('Charge failed: %s' % str(charge))
|
logger.info('Charge failed: %s' % str(charge))
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from django.views.generic.base import TemplateResponseMixin
|
|||||||
|
|
||||||
from pretix.base.models import CartPosition, Order
|
from pretix.base.models import CartPosition, Order
|
||||||
from pretix.base.models.orders import InvoiceAddress
|
from pretix.base.models.orders import InvoiceAddress
|
||||||
|
from pretix.base.services.mail import SendMailException
|
||||||
from pretix.base.services.orders import OrderError, perform_order
|
from pretix.base.services.orders import OrderError, perform_order
|
||||||
from pretix.base.signals import register_payment_providers
|
from pretix.base.signals import register_payment_providers
|
||||||
from pretix.multidomain.urlreverse import eventreverse
|
from pretix.multidomain.urlreverse import eventreverse
|
||||||
@@ -351,6 +352,8 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
|||||||
return exception['exc_message']
|
return exception['exc_message']
|
||||||
elif isinstance(exception, OrderError):
|
elif isinstance(exception, OrderError):
|
||||||
return str(exception)
|
return str(exception)
|
||||||
|
elif isinstance(exception, SendMailException):
|
||||||
|
return _('There was an error sending the confirmation mail. Please try again later.')
|
||||||
return super().get_error_message(exception)
|
return super().get_error_message(exception)
|
||||||
|
|
||||||
def get_error_url(self):
|
def get_error_url(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user