mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
Email: Allow to override backend for custom SMTP connections (#2368)
This commit is contained in:
@@ -25,6 +25,7 @@ from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
from itertools import groupby
|
||||
from smtplib import SMTPResponseException
|
||||
from typing import TypeVar
|
||||
|
||||
import css_inline
|
||||
from django.conf import settings
|
||||
@@ -49,23 +50,23 @@ from pretix.base.templatetags.rich_text import markdown_compile_email
|
||||
|
||||
logger = logging.getLogger('pretix.base.email')
|
||||
|
||||
T = TypeVar("T", bound=EmailBackend)
|
||||
|
||||
class CustomSMTPBackend(EmailBackend):
|
||||
|
||||
def test(self, from_addr):
|
||||
try:
|
||||
self.open()
|
||||
self.connection.ehlo_or_helo_if_needed()
|
||||
(code, resp) = self.connection.mail(from_addr, [])
|
||||
if code != 250:
|
||||
logger.warn('Error testing mail settings, code %d, resp: %s' % (code, resp))
|
||||
raise SMTPResponseException(code, resp)
|
||||
(code, resp) = self.connection.rcpt('testdummy@pretix.eu')
|
||||
if (code != 250) and (code != 251):
|
||||
logger.warn('Error testing mail settings, code %d, resp: %s' % (code, resp))
|
||||
raise SMTPResponseException(code, resp)
|
||||
finally:
|
||||
self.close()
|
||||
def test_custom_smtp_backend(backend: T, from_addr: str) -> None:
|
||||
try:
|
||||
backend.open()
|
||||
backend.connection.ehlo_or_helo_if_needed()
|
||||
(code, resp) = backend.connection.mail(from_addr, [])
|
||||
if code != 250:
|
||||
logger.warning('Error testing mail settings, code %d, resp: %s' % (code, resp))
|
||||
raise SMTPResponseException(code, resp)
|
||||
(code, resp) = backend.connection.rcpt('testdummy@pretix.eu')
|
||||
if (code != 250) and (code != 251):
|
||||
logger.warning('Error testing mail settings, code %d, resp: %s' % (code, resp))
|
||||
raise SMTPResponseException(code, resp)
|
||||
finally:
|
||||
backend.close()
|
||||
|
||||
|
||||
class BaseHTMLMailRenderer:
|
||||
|
||||
@@ -670,16 +670,17 @@ class Event(EventMixin, LoggedModel):
|
||||
Returns an email server connection, either by using the system-wide connection
|
||||
or by returning a custom one based on the event's settings.
|
||||
"""
|
||||
from pretix.base.email import CustomSMTPBackend
|
||||
|
||||
if self.settings.smtp_use_custom or force_custom:
|
||||
return CustomSMTPBackend(host=self.settings.smtp_host,
|
||||
port=self.settings.smtp_port,
|
||||
username=self.settings.smtp_username,
|
||||
password=self.settings.smtp_password,
|
||||
use_tls=self.settings.smtp_use_tls,
|
||||
use_ssl=self.settings.smtp_use_ssl,
|
||||
fail_silently=False, timeout=timeout)
|
||||
return get_connection(backend=settings.EMAIL_CUSTOM_SMTP_BACKEND,
|
||||
host=self.settings.smtp_host,
|
||||
port=self.settings.smtp_port,
|
||||
username=self.settings.smtp_username,
|
||||
password=self.settings.smtp_password,
|
||||
use_tls=self.settings.smtp_use_tls,
|
||||
use_ssl=self.settings.smtp_use_ssl,
|
||||
fail_silently=False,
|
||||
timeout=timeout)
|
||||
else:
|
||||
return get_connection(fail_silently=False)
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import string
|
||||
from datetime import date, datetime, time
|
||||
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.core.mail import get_connection
|
||||
from django.core.validators import MinLengthValidator, RegexValidator
|
||||
from django.db import models
|
||||
@@ -195,16 +196,15 @@ class Organizer(LoggedModel):
|
||||
Returns an email server connection, either by using the system-wide connection
|
||||
or by returning a custom one based on the organizer's settings.
|
||||
"""
|
||||
from pretix.base.email import CustomSMTPBackend
|
||||
|
||||
if self.settings.smtp_use_custom or force_custom:
|
||||
return CustomSMTPBackend(host=self.settings.smtp_host,
|
||||
port=self.settings.smtp_port,
|
||||
username=self.settings.smtp_username,
|
||||
password=self.settings.smtp_password,
|
||||
use_tls=self.settings.smtp_use_tls,
|
||||
use_ssl=self.settings.smtp_use_ssl,
|
||||
fail_silently=False, timeout=timeout)
|
||||
return get_connection(backend=settings.EMAIL_CUSTOM_SMTP_BACKEND,
|
||||
host=self.settings.smtp_host,
|
||||
port=self.settings.smtp_port,
|
||||
username=self.settings.smtp_username,
|
||||
password=self.settings.smtp_password,
|
||||
use_tls=self.settings.smtp_use_tls,
|
||||
use_ssl=self.settings.smtp_use_ssl,
|
||||
fail_silently=False, timeout=timeout)
|
||||
else:
|
||||
return get_connection(fail_silently=False)
|
||||
|
||||
|
||||
@@ -65,7 +65,9 @@ from i18nfield.utils import I18nJSONEncoder
|
||||
from pytz import timezone
|
||||
|
||||
from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.email import get_available_placeholders
|
||||
from pretix.base.email import (
|
||||
get_available_placeholders, test_custom_smtp_backend,
|
||||
)
|
||||
from pretix.base.models import Event, LogEntry, Order, TaxRule, Voucher
|
||||
from pretix.base.models.event import EventMetaValue
|
||||
from pretix.base.services import tickets
|
||||
@@ -641,7 +643,7 @@ class MailSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
if request.POST.get('test', '0').strip() == '1':
|
||||
backend = self.request.event.get_mail_backend(force_custom=True, timeout=10)
|
||||
try:
|
||||
backend.test(self.request.event.settings.mail_from)
|
||||
test_custom_smtp_backend(backend, self.request.event.settings.mail_from)
|
||||
except Exception as e:
|
||||
messages.warning(self.request, _('An error occurred while contacting the SMTP server: %s') % str(e))
|
||||
else:
|
||||
|
||||
@@ -64,6 +64,7 @@ from django.views.generic import (
|
||||
from pretix.api.models import WebHook
|
||||
from pretix.base.auth import get_auth_backends
|
||||
from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.email import test_custom_smtp_backend
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CachedFile, Customer, Device, Gate, GiftCard, Invoice, LogEntry,
|
||||
@@ -265,7 +266,7 @@ class OrganizerMailSettings(OrganizerSettingsFormView):
|
||||
if request.POST.get('test', '0').strip() == '1':
|
||||
backend = self.request.organizer.get_mail_backend(force_custom=True, timeout=10)
|
||||
try:
|
||||
backend.test(self.request.organizer.settings.mail_from)
|
||||
test_custom_smtp_backend(backend, self.request.organizer.settings.mail_from)
|
||||
except Exception as e:
|
||||
messages.warning(self.request, _('An error occurred while contacting the SMTP server: %s') % str(e))
|
||||
else:
|
||||
|
||||
@@ -222,6 +222,7 @@ EMAIL_HOST_PASSWORD = config.get('mail', 'password', fallback='')
|
||||
EMAIL_USE_TLS = config.getboolean('mail', 'tls', fallback=False)
|
||||
EMAIL_USE_SSL = config.getboolean('mail', 'ssl', fallback=False)
|
||||
EMAIL_SUBJECT_PREFIX = '[pretix] '
|
||||
EMAIL_BACKEND = EMAIL_CUSTOM_SMTP_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
|
||||
ADMINS = [('Admin', n) for n in config.get('mail', 'admins', fallback='').split(",") if n]
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ SITE_URL = "http://example.com"
|
||||
|
||||
atexit.register(tmpdir.cleanup)
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
|
||||
EMAIL_BACKEND = EMAIL_CUSTOM_SMTP_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
|
||||
|
||||
COMPRESS_ENABLED = COMPRESS_OFFLINE = False
|
||||
COMPRESS_CACHE_BACKEND = 'testcache'
|
||||
|
||||
@@ -81,7 +81,34 @@ def test_send_mail_with_event_sender(env):
|
||||
assert len(djmail.outbox) == 1
|
||||
assert djmail.outbox[0].to == [user.email]
|
||||
assert djmail.outbox[0].subject == 'Test subject'
|
||||
assert djmail.outbox[0].from_email == 'Dummy <foo@bar>'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("smtp_use_custom", (True, False))
|
||||
def test_send_mail_custom_event_smtp(env, smtp_use_custom):
|
||||
djmail.outbox = []
|
||||
event, user, organizer = env
|
||||
event.settings.set("smtp_use_custom", smtp_use_custom)
|
||||
|
||||
mail('dummy@dummy.dummy', 'Test subject', 'mailtest.txt', {}, event=event)
|
||||
|
||||
assert len(djmail.outbox) == 1
|
||||
assert djmail.outbox[0].to == [user.email]
|
||||
assert djmail.outbox[0].subject == 'Test subject'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("smtp_use_custom", (True, False))
|
||||
def test_send_mail_custom_organizer_smtp(env, smtp_use_custom):
|
||||
djmail.outbox = []
|
||||
event, user, organizer = env
|
||||
organizer.settings.set("smtp_use_custom", smtp_use_custom)
|
||||
|
||||
mail('dummy@dummy.dummy', 'Test subject', 'mailtest.txt', {}, organizer=organizer)
|
||||
|
||||
assert len(djmail.outbox) == 1
|
||||
assert djmail.outbox[0].to == [user.email]
|
||||
assert djmail.outbox[0].subject == 'Test subject'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
@@ -501,8 +501,7 @@ class EventsTest(SoupTest):
|
||||
|
||||
def test_email_settings(self):
|
||||
with mocker_context() as mocker:
|
||||
mocked = mocker.patch('pretix.base.email.CustomSMTPBackend.test')
|
||||
|
||||
mocked = mocker.patch('pretix.control.views.event.test_custom_smtp_backend')
|
||||
doc = self.get_doc('/control/event/%s/%s/settings/email' % (self.orga1.slug, self.event1.slug))
|
||||
data = extract_form_fields(doc.select("form")[0])
|
||||
data['test'] = '1'
|
||||
|
||||
@@ -27,6 +27,7 @@ from django_scopes import scopes_disabled
|
||||
from tests.base import SoupTest, extract_form_fields
|
||||
|
||||
from pretix.base.models import Event, Organizer, Team, User
|
||||
from pretix.testutils.mock import mocker_context
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -97,3 +98,15 @@ class OrganizerTest(SoupTest):
|
||||
self.orga1.settings.flush()
|
||||
assert self.orga1.settings.primary_color == "#33c33c"
|
||||
assert called
|
||||
|
||||
def test_email_settings(self):
|
||||
with mocker_context() as mocker:
|
||||
mocked = mocker.patch('pretix.control.views.organizer.test_custom_smtp_backend')
|
||||
doc = self.get_doc('/control/organizer/%s/settings/email' % self.orga1.slug)
|
||||
data = extract_form_fields(doc.select("form")[0])
|
||||
data['test'] = '1'
|
||||
doc = self.post_doc('/control/organizer/%s/settings/email' % self.orga1.slug,
|
||||
data, follow=True)
|
||||
assert doc.select('.alert-success')
|
||||
self.event1.settings.flush()
|
||||
assert mocked.called
|
||||
|
||||
Reference in New Issue
Block a user