forked from GithubBackups/healthchecks
"Unsubscribe" link in alert emails. Fixes #111
This commit is contained in:
parent
c16eeda004
commit
6a1c5dd3b7
@ -229,6 +229,11 @@ class Channel(models.Model):
|
|||||||
verify_link = settings.SITE_ROOT + verify_link
|
verify_link = settings.SITE_ROOT + verify_link
|
||||||
emails.verify_email(self.value, {"verify_link": verify_link})
|
emails.verify_email(self.value, {"verify_link": verify_link})
|
||||||
|
|
||||||
|
def get_unsub_link(self):
|
||||||
|
args = [self.code, self.make_token()]
|
||||||
|
verify_link = reverse("hc-unsubscribe-alerts", args=args)
|
||||||
|
return settings.SITE_ROOT + verify_link
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def transport(self):
|
def transport(self):
|
||||||
if self.kind == "email":
|
if self.kind == "email":
|
||||||
|
@ -146,23 +146,6 @@ class NotifyTestCase(BaseTestCase):
|
|||||||
self.assertEqual(n.error, "Email not verified")
|
self.assertEqual(n.error, "Email not verified")
|
||||||
self.assertEqual(len(mail.outbox), 0)
|
self.assertEqual(len(mail.outbox), 0)
|
||||||
|
|
||||||
@override_settings(USE_PAYMENTS=True)
|
|
||||||
def test_email_contains_upgrade_notice(self):
|
|
||||||
self._setup_data("email", "alice@example.org", status="up")
|
|
||||||
self.profile.team_access_allowed = False
|
|
||||||
self.profile.save()
|
|
||||||
|
|
||||||
self.channel.notify(self.check)
|
|
||||||
|
|
||||||
n = Notification.objects.get()
|
|
||||||
self.assertEqual(n.error, "")
|
|
||||||
|
|
||||||
# Check is up, payments are enabled, and the user does not have team
|
|
||||||
# access: the email should contain upgrade note
|
|
||||||
message = mail.outbox[0]
|
|
||||||
html, _ = message.alternatives[0]
|
|
||||||
assert "/pricing/" in html
|
|
||||||
|
|
||||||
@patch("hc.api.transports.requests.request")
|
@patch("hc.api.transports.requests.request")
|
||||||
def test_pd(self, mock_post):
|
def test_pd(self, mock_post):
|
||||||
self._setup_data("pd", "123")
|
self._setup_data("pd", "123")
|
||||||
|
@ -46,16 +46,11 @@ class Email(Transport):
|
|||||||
if not self.channel.email_verified:
|
if not self.channel.email_verified:
|
||||||
return "Email not verified"
|
return "Email not verified"
|
||||||
|
|
||||||
show_upgrade_note = False
|
|
||||||
if settings.USE_PAYMENTS and check.status == "up":
|
|
||||||
if not check.user.profile.team_access_allowed:
|
|
||||||
show_upgrade_note = True
|
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
"check": check,
|
"check": check,
|
||||||
"checks": self.checks(),
|
"checks": self.checks(),
|
||||||
"now": timezone.now(),
|
"now": timezone.now(),
|
||||||
"show_upgrade_note": show_upgrade_note
|
"unsub_link": self.channel.get_unsub_link()
|
||||||
}
|
}
|
||||||
emails.alert(self.channel.value, ctx)
|
emails.alert(self.channel.value, ctx)
|
||||||
|
|
||||||
|
38
hc/front/tests/test_unsubscribe_email.py
Normal file
38
hc/front/tests/test_unsubscribe_email.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from hc.api.models import Channel
|
||||||
|
from hc.test import BaseTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class UnsubscribeEmailTestCase(BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(UnsubscribeEmailTestCase, self).setUp()
|
||||||
|
self.channel = Channel(user=self.alice, kind="email")
|
||||||
|
self.channel.value = "alice@example.org"
|
||||||
|
self.channel.save()
|
||||||
|
|
||||||
|
def test_it_works(self):
|
||||||
|
token = self.channel.make_token()
|
||||||
|
url = "/integrations/%s/unsub/%s/" % (self.channel.code, token)
|
||||||
|
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertContains(r, "has been unsubscribed", status_code=200)
|
||||||
|
|
||||||
|
q = Channel.objects.filter(code=self.channel.code)
|
||||||
|
self.assertEqual(q.count(), 0)
|
||||||
|
|
||||||
|
def test_it_checks_token(self):
|
||||||
|
url = "/integrations/%s/unsub/faketoken/" % self.channel.code
|
||||||
|
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertContains(r, "link you just used is incorrect",
|
||||||
|
status_code=200)
|
||||||
|
|
||||||
|
def test_it_checks_channel_kind(self):
|
||||||
|
self.channel.kind = "webhook"
|
||||||
|
self.channel.save()
|
||||||
|
|
||||||
|
token = self.channel.make_token()
|
||||||
|
url = "/integrations/%s/unsub/%s/" % (self.channel.code, token)
|
||||||
|
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 400)
|
@ -14,7 +14,7 @@ class VerifyEmailTestCase(BaseTestCase):
|
|||||||
token = self.channel.make_token()
|
token = self.channel.make_token()
|
||||||
url = "/integrations/%s/verify/%s/" % (self.channel.code, token)
|
url = "/integrations/%s/verify/%s/" % (self.channel.code, token)
|
||||||
|
|
||||||
r = self.client.post(url)
|
r = self.client.get(url)
|
||||||
assert r.status_code == 200, r.status_code
|
assert r.status_code == 200, r.status_code
|
||||||
|
|
||||||
channel = Channel.objects.get(code=self.channel.code)
|
channel = Channel.objects.get(code=self.channel.code)
|
||||||
@ -23,7 +23,7 @@ class VerifyEmailTestCase(BaseTestCase):
|
|||||||
def test_it_handles_bad_token(self):
|
def test_it_handles_bad_token(self):
|
||||||
url = "/integrations/%s/verify/bad-token/" % self.channel.code
|
url = "/integrations/%s/verify/bad-token/" % self.channel.code
|
||||||
|
|
||||||
r = self.client.post(url)
|
r = self.client.get(url)
|
||||||
assert r.status_code == 200, r.status_code
|
assert r.status_code == 200, r.status_code
|
||||||
|
|
||||||
channel = Channel.objects.get(code=self.channel.code)
|
channel = Channel.objects.get(code=self.channel.code)
|
||||||
@ -35,5 +35,5 @@ class VerifyEmailTestCase(BaseTestCase):
|
|||||||
token = self.channel.make_token()
|
token = self.channel.make_token()
|
||||||
url = "/integrations/%s/verify/%s/" % (code, token)
|
url = "/integrations/%s/verify/%s/" % (code, token)
|
||||||
|
|
||||||
r = self.client.post(url)
|
r = self.client.get(url)
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
|
@ -27,6 +27,8 @@ channel_urls = [
|
|||||||
url(r'^([\w-]+)/remove/$', views.remove_channel, name="hc-remove-channel"),
|
url(r'^([\w-]+)/remove/$', views.remove_channel, name="hc-remove-channel"),
|
||||||
url(r'^([\w-]+)/verify/([\w-]+)/$', views.verify_email,
|
url(r'^([\w-]+)/verify/([\w-]+)/$', views.verify_email,
|
||||||
name="hc-verify-email"),
|
name="hc-verify-email"),
|
||||||
|
url(r'^([\w-]+)/unsub/([\w-]+)/$', views.unsubscribe_email,
|
||||||
|
name="hc-unsubscribe-alerts"),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -361,6 +361,19 @@ def verify_email(request, code, token):
|
|||||||
return render(request, "bad_link.html")
|
return render(request, "bad_link.html")
|
||||||
|
|
||||||
|
|
||||||
|
@uuid_or_400
|
||||||
|
def unsubscribe_email(request, code, token):
|
||||||
|
channel = get_object_or_404(Channel, code=code)
|
||||||
|
if channel.make_token() != token:
|
||||||
|
return render(request, "bad_link.html")
|
||||||
|
|
||||||
|
if channel.kind != "email":
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
channel.delete()
|
||||||
|
return render(request, "front/unsubscribe_success.html")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@uuid_or_400
|
@uuid_or_400
|
||||||
def remove_channel(request, code):
|
def remove_channel(request, code):
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-sm-offset-3">
|
<div class="col-sm-6 col-sm-offset-3">
|
||||||
<div class="hc-dialog">
|
<div class="hc-dialog">
|
||||||
<h1>Incorrect Verification Link</h1>
|
<h1>Incorrect Confirmation Link</h1>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<p>
|
<p>
|
||||||
The verification link you just used is incorrect.
|
The confirmation link you just used is incorrect.
|
||||||
If you copy-pasted the link, please make sure you did not
|
If you copy-pasted the link, please make sure you did not
|
||||||
miss any characters.
|
miss any characters.
|
||||||
</p>
|
</p>
|
||||||
|
@ -15,13 +15,13 @@ Here is a summary of all your checks:
|
|||||||
|
|
||||||
{% include "emails/summary-html.html" %}
|
{% include "emails/summary-html.html" %}
|
||||||
|
|
||||||
{% if show_upgrade_note %}
|
|
||||||
<strong>P.S.</strong>
|
|
||||||
Find this service useful? Support it by upgrading to
|
|
||||||
a <a href="https://healthchecks.io/pricing/">premium account</a>!
|
|
||||||
<br /><br />
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
Thanks,<br>
|
Thanks,<br>
|
||||||
The {% escaped_site_name %} Team
|
The {% escaped_site_name %} Team
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block unsub %}
|
||||||
|
<br>
|
||||||
|
<a href="{{ unsub_link }}" target="_blank" style="color: #666666; text-decoration: underline;">
|
||||||
|
Unsubscribe
|
||||||
|
</a>
|
||||||
|
{% endblock %}
|
||||||
|
19
templates/front/unsubscribe_success.html
Normal file
19
templates/front/unsubscribe_success.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load hc_extras %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 col-sm-offset-3">
|
||||||
|
<div class="hc-dialog">
|
||||||
|
<h1>Unsubscribed</h1>
|
||||||
|
<div class="dialog-body">
|
||||||
|
<p>
|
||||||
|
Your email address has been unsubscribed from
|
||||||
|
{% site_name %} notifications.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user