Implement opt-out for email notifications
This commit is contained in:
parent
bc4bc55025
commit
c9045f5b1f
@ -81,6 +81,15 @@ class Comments:
|
|||||||
' mode=1',
|
' mode=1',
|
||||||
'WHERE id=? AND mode=2'], (id, ))
|
'WHERE id=? AND mode=2'], (id, ))
|
||||||
|
|
||||||
|
def unsubscribe(self, id):
|
||||||
|
"""
|
||||||
|
Turn off email notifications for replies to this comment.
|
||||||
|
"""
|
||||||
|
self.db.execute([
|
||||||
|
'UPDATE comments SET',
|
||||||
|
' notification=0',
|
||||||
|
'WHERE id=?'], (id, ))
|
||||||
|
|
||||||
def update(self, id, data):
|
def update(self, id, data):
|
||||||
"""
|
"""
|
||||||
Update comment :param:`id` with values from :param:`data` and return
|
Update comment :param:`id` with values from :param:`data` and return
|
||||||
|
@ -93,7 +93,7 @@ class SMTP(object):
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
yield "comments.new:after-save", self.notify
|
yield "comments.new:after-save", self.notify
|
||||||
|
|
||||||
def format(self, thread, comment, admin=False):
|
def format(self, thread, comment, comment_parent, admin=False):
|
||||||
|
|
||||||
rv = io.StringIO()
|
rv = io.StringIO()
|
||||||
|
|
||||||
@ -115,17 +115,23 @@ class SMTP(object):
|
|||||||
rv.write("Link to comment: %s\n" %
|
rv.write("Link to comment: %s\n" %
|
||||||
(local("origin") + thread["uri"] + "#isso-%i" % comment["id"]))
|
(local("origin") + thread["uri"] + "#isso-%i" % comment["id"]))
|
||||||
rv.write("\n")
|
rv.write("\n")
|
||||||
|
rv.write("---\n")
|
||||||
|
|
||||||
if admin:
|
if admin:
|
||||||
uri = local("host") + "/id/%i" % comment["id"]
|
uri = local("host") + "/id/%i" % comment["id"]
|
||||||
key = self.isso.sign(comment["id"])
|
key = self.isso.sign(comment["id"])
|
||||||
|
|
||||||
rv.write("---\n")
|
|
||||||
rv.write("Delete comment: %s\n" % (uri + "/delete/" + key))
|
rv.write("Delete comment: %s\n" % (uri + "/delete/" + key))
|
||||||
|
|
||||||
if comment["mode"] == 2:
|
if comment["mode"] == 2:
|
||||||
rv.write("Activate comment: %s\n" % (uri + "/activate/" + key))
|
rv.write("Activate comment: %s\n" % (uri + "/activate/" + key))
|
||||||
|
|
||||||
|
else:
|
||||||
|
uri = local("host") + "/id/%i" % comment_parent["id"]
|
||||||
|
key = self.isso.sign(('unsubscribe', comment_parent["id"]))
|
||||||
|
|
||||||
|
rv.write("Unsubscribe from this conversation: %s\n" % (uri + "/unsubscribe/" + key))
|
||||||
|
|
||||||
rv.seek(0)
|
rv.seek(0)
|
||||||
return rv.read()
|
return rv.read()
|
||||||
|
|
||||||
@ -134,11 +140,11 @@ class SMTP(object):
|
|||||||
comment_parent = self.isso.db.comments.get(comment["parent"])
|
comment_parent = self.isso.db.comments.get(comment["parent"])
|
||||||
# Notify the author that a new comment is posted if requested
|
# Notify the author that a new comment is posted if requested
|
||||||
if comment_parent and "email" in comment_parent and comment_parent["notification"]:
|
if comment_parent and "email" in comment_parent and comment_parent["notification"]:
|
||||||
body = self.format(thread, comment, admin=False)
|
body = self.format(thread, comment, comment_parent, admin=False)
|
||||||
subject = "Re: New comment posted on %s" % thread["title"]
|
subject = "Re: New comment posted on %s" % thread["title"]
|
||||||
self.sendmail(subject, body, thread, comment, to=comment_parent["email"])
|
self.sendmail(subject, body, thread, comment, to=comment_parent["email"])
|
||||||
|
|
||||||
body = self.format(thread, comment, admin=True)
|
body = self.format(thread, comment, None, admin=True)
|
||||||
self.sendmail(thread["title"], body, thread, comment)
|
self.sendmail(thread["title"], body, thread, comment)
|
||||||
|
|
||||||
def sendmail(self, subject, body, thread, comment, to=None):
|
def sendmail(self, subject, body, thread, comment, to=None):
|
||||||
|
@ -87,21 +87,22 @@ class API(object):
|
|||||||
ACCEPT = set(['text', 'author', 'website', 'email', 'parent', 'title', 'notification'])
|
ACCEPT = set(['text', 'author', 'website', 'email', 'parent', 'title', 'notification'])
|
||||||
|
|
||||||
VIEWS = [
|
VIEWS = [
|
||||||
('fetch', ('GET', '/')),
|
('fetch', ('GET', '/')),
|
||||||
('new', ('POST', '/new')),
|
('new', ('POST', '/new')),
|
||||||
('count', ('GET', '/count')),
|
('count', ('GET', '/count')),
|
||||||
('counts', ('POST', '/count')),
|
('counts', ('POST', '/count')),
|
||||||
('view', ('GET', '/id/<int:id>')),
|
('view', ('GET', '/id/<int:id>')),
|
||||||
('edit', ('PUT', '/id/<int:id>')),
|
('edit', ('PUT', '/id/<int:id>')),
|
||||||
('delete', ('DELETE', '/id/<int:id>')),
|
('delete', ('DELETE', '/id/<int:id>')),
|
||||||
('moderate', ('GET', '/id/<int:id>/<any(edit,activate,delete):action>/<string:key>')),
|
('unsubscribe', ('GET', '/id/<int:id>/unsubscribe/<string:key>')),
|
||||||
('moderate', ('POST', '/id/<int:id>/<any(edit,activate,delete):action>/<string:key>')),
|
('moderate', ('GET', '/id/<int:id>/<any(edit,activate,delete):action>/<string:key>')),
|
||||||
('like', ('POST', '/id/<int:id>/like')),
|
('moderate', ('POST', '/id/<int:id>/<any(edit,activate,delete):action>/<string:key>')),
|
||||||
('dislike', ('POST', '/id/<int:id>/dislike')),
|
('like', ('POST', '/id/<int:id>/like')),
|
||||||
('demo', ('GET', '/demo')),
|
('dislike', ('POST', '/id/<int:id>/dislike')),
|
||||||
('preview', ('POST', '/preview')),
|
('demo', ('GET', '/demo')),
|
||||||
('login', ('POST', '/login')),
|
('preview', ('POST', '/preview')),
|
||||||
('admin', ('GET', '/admin'))
|
('login', ('POST', '/login')),
|
||||||
|
('admin', ('GET', '/admin'))
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, isso, hasher):
|
def __init__(self, isso, hasher):
|
||||||
@ -477,6 +478,66 @@ class API(object):
|
|||||||
resp.headers.add("X-Set-Cookie", cookie("isso-%i" % id))
|
resp.headers.add("X-Set-Cookie", cookie("isso-%i" % id))
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
"""
|
||||||
|
@api {get} /id/:id/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} 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'
|
||||||
|
|
||||||
|
@apiSuccessExample {html} Using GET:
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
if (confirm('Delete: Are you sure?')) {
|
||||||
|
xhr = new XMLHttpRequest;
|
||||||
|
xhr.open('POST', window.location.href);
|
||||||
|
xhr.send(null);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@apiSuccessExample Using POST:
|
||||||
|
Yo
|
||||||
|
"""
|
||||||
|
|
||||||
|
def unsubscribe(self, environ, request, id, key):
|
||||||
|
try:
|
||||||
|
rv = self.isso.unsign(key, max_age=2**32)
|
||||||
|
except (BadSignature, SignatureExpired):
|
||||||
|
raise Forbidden
|
||||||
|
|
||||||
|
if rv[0] != 'unsubscribe' or rv[1] != id:
|
||||||
|
raise Forbidden
|
||||||
|
|
||||||
|
item = self.comments.get(id)
|
||||||
|
|
||||||
|
if item is None:
|
||||||
|
raise NotFound
|
||||||
|
|
||||||
|
with self.isso.lock:
|
||||||
|
self.comments.unsubscribe(id)
|
||||||
|
|
||||||
|
modal = (
|
||||||
|
"<!DOCTYPE html>"
|
||||||
|
"<html>"
|
||||||
|
"<head>"
|
||||||
|
" <title>Successfully unsubscribed</title>"
|
||||||
|
"</head>"
|
||||||
|
"<body>"
|
||||||
|
" <p>You have been unsubscribed from replies in the given conversation.</p>"
|
||||||
|
"</body>"
|
||||||
|
"</html>")
|
||||||
|
|
||||||
|
return Response(modal, 200, content_type="text/html")
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@api {post} /id/:id/:action/key moderate
|
@api {post} /id/:id/:action/key moderate
|
||||||
@apiGroup Comment
|
@apiGroup Comment
|
||||||
|
Loading…
Reference in New Issue
Block a user