Make unsubscribe work with notifications for replies

This commit is contained in:
Pelle Nilsson 2018-05-10 20:48:13 +02:00
parent f6f61c547a
commit 0063fd6e88
3 changed files with 28 additions and 15 deletions

View File

@ -81,14 +81,14 @@ class Comments:
' mode=1',
'WHERE id=? AND mode=2'], (id, ))
def unsubscribe(self, id):
def unsubscribe(self, email, id):
"""
Turn off email notifications for replies to this comment.
"""
self.db.execute([
'UPDATE comments SET',
' notification=0',
'WHERE id=?'], (id, ))
'WHERE email=? AND (id=? OR parent=?);'], (email, id, id))
def update(self, id, data):
"""

View File

@ -14,6 +14,11 @@ from email.utils import formatdate
from email.header import Header
from email.mime.text import MIMEText
try:
from urllib.parse import quote
except ImportError:
from urllib import quote
import logging
logger = logging.getLogger("isso")
@ -99,7 +104,7 @@ class SMTP(object):
def __iter__(self):
yield "comments.new:after-save", self.notify
def format(self, thread, comment, comment_parent, admin=False):
def format(self, thread, comment, parent_comment, recipient=None, admin=False):
rv = io.StringIO()
@ -133,10 +138,10 @@ class SMTP(object):
rv.write("Activate comment: %s\n" % (uri + "/activate/" + key))
else:
uri = self.general_host + "/id/%i" % comment_parent["id"]
key = self.isso.sign(('unsubscribe', comment_parent["id"]))
uri = self.general_host + "/id/%i" % parent_comment["id"]
key = self.isso.sign(('unsubscribe', recipient))
rv.write("Unsubscribe from this conversation: %s\n" % (uri + "/unsubscribe/" + key))
rv.write("Unsubscribe from this conversation: %s\n" % (uri + "/unsubscribe/" + quote(recipient) + "/" + key))
rv.seek(0)
return rv.read()
@ -152,10 +157,10 @@ class SMTP(object):
email = comment_to_notify["email"]
if "email" in comment_to_notify and comment_to_notify["notification"] and email not in notified \
and comment_to_notify["id"] != comment["id"] and email != comment["email"]:
body = self.format(thread, comment, comment_to_notify, admin=False)
body = self.format(thread, comment, parent_comment, email, admin=False)
subject = "Re: New comment posted on %s" % thread["title"]
self.sendmail(subject, body, thread, comment, to=email)
notified += email
notified.append(email)
body = self.format(thread, comment, None, admin=True)
self.sendmail(thread["title"], body, thread, comment)

View File

@ -32,6 +32,10 @@ try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
try:
from urllib import unquote
except ImportError:
from urllib.parse import unquote
try:
from StringIO import StringIO
except ImportError:
@ -107,7 +111,7 @@ class API(object):
('view', ('GET', '/id/<int:id>')),
('edit', ('PUT', '/id/<int:id>')),
('delete', ('DELETE', '/id/<int:id>')),
('unsubscribe', ('GET', '/id/<int:id>/unsubscribe/<string:key>')),
('unsubscribe', ('GET', '/id/<int:id>/unsubscribe/<string:email>/<string:key>')),
('moderate', ('GET', '/id/<int:id>/<any(edit,activate,delete):action>/<string:key>')),
('moderate', ('POST', '/id/<int:id>/<any(edit,activate,delete):action>/<string:key>')),
('like', ('POST', '/id/<int:id>/like')),
@ -494,18 +498,20 @@ class API(object):
return resp
"""
@api {get} /id/:id/key unsubscribe
@api {get} /id/:id/:email/key unsubscribe
@apiGroup Comment
@apiDescription
Opt out from getting any further email notifications about replies to a particular comment. In order to use this endpoint, the requestor needs a `key` that is usually obtained from an email sent out by isso.
@apiParam {number} id
The id of the comment to unsubscribe from replies to.
@apiParam {string} email
The email address of the subscriber.
@apiParam {string} key
The key to authenticate the subscriber.
@apiExample {curl} Unsubscribe from replies to comment with id 13:
curl -X GET 'https://comments.example.com/id/13/unsubscribe/TODO-COMPUTE-HASH'
@apiExample {curl} Unsubscribe Alice from replies to comment with id 13:
curl -X GET 'https://comments.example.com/id/13/unsubscribe/alice@example.com/TODO-COMPUTE-HASH'
@apiSuccessExample {html} Using GET:
&lt;!DOCTYPE html&gt;
@ -523,13 +529,15 @@ class API(object):
Yo
"""
def unsubscribe(self, environ, request, id, key):
def unsubscribe(self, environ, request, id, email, key):
email = unquote(email)
try:
rv = self.isso.unsign(key, max_age=2**32)
except (BadSignature, SignatureExpired):
raise Forbidden
if rv[0] != 'unsubscribe' or rv[1] != id:
if rv[0] != 'unsubscribe' or rv[1] != email:
raise Forbidden
item = self.comments.get(id)
@ -538,7 +546,7 @@ class API(object):
raise NotFound
with self.isso.lock:
self.comments.unsubscribe(id)
self.comments.unsubscribe(email, id)
modal = (
"<!DOCTYPE html>"