fix unsubscription link computation for each destination (#16456)

This commit is contained in:
Serghei Mihai 2017-05-23 12:00:35 +02:00
parent 2949280eec
commit 23c19695b1
3 changed files with 67 additions and 25 deletions

View File

@ -36,7 +36,7 @@ UNSUBSCRIBE_LINK_PLACEHOLDER = '##UNSUBSCRIBE_LINK_PLACEHOLDER##'
def transform_image_src(src, **kwargs):
return urlparse.urljoin(settings.SITE_BASE_URL, src)
def send_email(title, content, destinations, category_id=None):
def send_email(title, content, destinations, category_id):
logger = logging.getLogger(__name__)
total_sent = 0
handler = HTML2Text()
@ -51,13 +51,15 @@ def send_email(title, content, destinations, category_id=None):
message.transformer.apply_to_images(func=transform_image_src)
message.transformer.load_and_transform(images_inline=True)
message.transformer.save()
orig_html = message.html
for dest in destinations:
if category_id:
unsubscribe_token = signing.dumps({'category': category_id,
'identifier': dest})
unsubscribe_link = urlparse.urljoin(settings.SITE_BASE_URL, reverse(
'unsubscribe', kwargs={'unsubscription_token': unsubscribe_token}))
message.html = message.html.replace(UNSUBSCRIBE_LINK_PLACEHOLDER, unsubscribe_link)
unsubscribe_token = signing.dumps({'category': category_id,
'identifier': dest})
unsubscribe_link = urlparse.urljoin(settings.SITE_BASE_URL, reverse(
'unsubscribe', kwargs={'unsubscription_token': unsubscribe_token}))
message.html = orig_html.replace(UNSUBSCRIBE_LINK_PLACEHOLDER, unsubscribe_link)
handler.body_width = 0
message.text = handler.handle(message.html)
sent = message.send(to=dest)

View File

@ -1,6 +1,40 @@
import pytest
import django_webtest
import copy
import django.core.mail.backends.locmem
from django.core.mail.backends.locmem import EmailBackend as DjangoEmailBackend
class MockedEmailBackend(object):
def create_email_backend(self, *args, **kwargs):
class EmailBackend(DjangoEmailBackend):
def __init__(self, *args, **kwargs):
super(DjangoEmailBackend, self).__init__(*args, **kwargs)
def send_messages(self, messages):
new_messages = []
for message in messages:
new_messages.append(copy.deepcopy(message))
return super(EmailBackend, self).send_messages(new_messages)
return EmailBackend
def __enter__(self):
import django.core.mail.backends.locmem
self.locmem_emailbackend = django.core.mail.backends.locmem.EmailBackend
django.core.mail.backends.locmem.EmailBackend = self.create_email_backend()
return self
def __exit__(self, *args, **kwargs):
django.core.mail.backends.locmem.EmailBackend = self.locmem_emailbackend
@pytest.fixture
def custom_mailoutbox():
with MockedEmailBackend() as mock:
yield mock
@pytest.fixture
def app(request):

View File

@ -111,30 +111,36 @@ def test_check_inline_images(mocked_get, app, categories, announces, mailoutbox)
storage.delete(image_name)
def test_unsubscription_link(app, categories, announces, mailoutbox):
def test_unsubscription_link(app, categories, announces, custom_mailoutbox):
unsubscription_link_sentinel = ''
subscriptions_number = 3
scheme = 'mailto:'
for category in categories:
uuid = uuid4()
scheme = 'mailto:'
uri = scheme + '%s@example.net' % uuid
Subscription.objects.create(category=category, identifier=uri, uuid=str(uuid))
for i in xrange(subscriptions_number):
uuid = uuid4()
uri = scheme + '%s@example.com' % uuid
Subscription.objects.create(category=category, identifier=uri, uuid=str(uuid))
for i, announce in enumerate(announces):
if announce.category != category:
continue
broadcast = Broadcast.objects.get(announce=announce)
broadcast.send()
assert broadcast.delivery_count
assert len(mailoutbox) == i+1
assert len(mail.outbox) == (i+1)*subscriptions_number
assert mail.outbox[i*subscriptions_number].subject == announce.title
signature = urllib.unquote(re.findall('/unsubscribe/(.*)"', mailoutbox[i].html)[0])
unsubscription_link = reverse('unsubscribe', kwargs={'unsubscription_token': signature})
assert signing.loads(signature) == {
'category': announce.category.pk, 'identifier': uri}
assert mailoutbox[i].subject == announce.title
assert unsubscription_link in mailoutbox[i].html
assert unsubscription_link in mailoutbox[i].text
assert unsubscription_link_sentinel != unsubscription_link
# make sure the uri schema is not in the page
resp = app.get(unsubscription_link)
assert scheme not in resp.content
unsubscription_link_sentinel = unsubscription_link
for counter, destination in enumerate(category.subscription_set.all()):
index = i*subscriptions_number+counter
signature = urllib.unquote(re.findall('/unsubscribe/(.*)"', mail.outbox[index].html)[0])
unsubscription_link = reverse('unsubscribe', kwargs={'unsubscription_token': signature})
assert unsubscription_link in mail.outbox[index].html
assert unsubscription_link in mail.outbox[index].text
assert unsubscription_link_sentinel != unsubscription_link
assert signing.loads(signature) == {
'category': announce.category.pk, 'identifier': destination.identifier}
unsubscription_link_sentinel = unsubscription_link
# make sure the uri schema is not in the page
resp = app.get(unsubscription_link)
assert scheme not in resp.content