Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reply notification #50

Merged
merged 3 commits into from Jul 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions isso/css/isso.scss
Expand Up @@ -214,5 +214,11 @@ a {
}
}
}

> .notification-section {
display: none;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invisible by default? Why?

padding-bottom: 10px;
}

}
}
11 changes: 6 additions & 5 deletions isso/db/comments.py
Expand Up @@ -20,7 +20,7 @@ class Comments:
"""

fields = ['tid', 'id', 'parent', 'created', 'modified', 'mode', 'remote_addr',
'text', 'author', 'email', 'website', 'likes', 'dislikes', 'voters']
'text', 'author', 'email', 'website', 'likes', 'dislikes', 'voters', 'notification']

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the name "notification", better the plural "notifications" or the verb "notify" to apply for (multiple) notifications.

def __init__(self, db):

Expand All @@ -30,7 +30,8 @@ def __init__(self, db):
' tid REFERENCES threads(id), id INTEGER PRIMARY KEY, parent INTEGER,',
' created FLOAT NOT NULL, modified FLOAT, mode INTEGER, remote_addr VARCHAR,',
' text VARCHAR, author VARCHAR, email VARCHAR, website VARCHAR,',
' likes INTEGER DEFAULT 0, dislikes INTEGER DEFAULT 0, voters BLOB NOT NULL);'])
' likes INTEGER DEFAULT 0, dislikes INTEGER DEFAULT 0, voters BLOB NOT NULL,',
' notification INTEGER);'])

def add(self, uri, c):
"""
Expand All @@ -41,16 +42,16 @@ def add(self, uri, c):
'INSERT INTO comments (',
' tid, parent,'
' created, modified, mode, remote_addr,',
' text, author, email, website, voters )',
' text, author, email, website, voters, notification)',
'SELECT',
' threads.id, ?,',
' ?, ?, ?, ?,',
' ?, ?, ?, ?, ?',
' ?, ?, ?, ?, ?, ?',
'FROM threads WHERE threads.uri = ?;'], (
c.get('parent'),
c.get('created') or time.time(), None, c["mode"], c['remote_addr'],
c['text'], c.get('author'), c.get('email'), c.get('website'), buffer(
Bloomfilter(iterable=[c['remote_addr']]).array),
Bloomfilter(iterable=[c['remote_addr']]).array), c.get('notification', 0),
uri)
)

Expand Down
58 changes: 34 additions & 24 deletions isso/ext/notifications.py
Expand Up @@ -59,9 +59,8 @@ def spooler(args):
def __enter__(self):
klass = (smtplib.SMTP_SSL if self.conf.get('security') == 'ssl' else smtplib.SMTP)
self.client = klass(host=self.conf.get('host'), port=self.conf.getint('port'))

if self.conf.get('security') == 'starttls':
self.client.starttls();
self.client.starttls()

if self.conf.get('username') and self.conf.get('password'):
self.client.login(self.conf.get('username'),
Expand All @@ -75,7 +74,7 @@ def __exit__(self, exc_type, exc_value, traceback):
def __iter__(self):
yield "comments.new:after-save", self.notify

def format(self, thread, comment):
def format(self, thread, comment, admin=False):

rv = io.StringIO()

Expand All @@ -88,39 +87,50 @@ def format(self, thread, comment):
rv.write(comment["text"] + "\n")
rv.write("\n")

if comment["website"]:
rv.write("User's URL: %s\n" % comment["website"])
if admin:
if comment["website"]:
rv.write("User's URL: %s\n" % comment["website"])

rv.write("IP address: %s\n" % comment["remote_addr"])
rv.write("Link to comment: %s\n" % (local("origin") + thread["uri"] + "#isso-%i" % comment["id"]))
rv.write("\n")
rv.write("IP address: %s\n" % comment["remote_addr"])
rv.write("Link to comment: %s\n" % (local("origin") + thread["uri"] + "#isso-%i" % comment["id"]))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A link to the comment in the reply notification would be useful.

rv.write("\n")

uri = local("host") + "/id/%i" % comment["id"]
key = self.isso.sign(comment["id"])
uri = local("host") + "/id/%i" % comment["id"]
key = self.isso.sign(comment["id"])

rv.write("---\n")
rv.write("Delete comment: %s\n" % (uri + "/delete/" + key))
rv.write("---\n")
rv.write("Delete comment: %s\n" % (uri + "/delete/" + key))

if comment["mode"] == 2:
rv.write("Activate comment: %s\n" % (uri + "/activate/" + key))
if comment["mode"] == 2:
rv.write("Activate comment: %s\n" % (uri + "/activate/" + key))

rv.seek(0)
return rv.read()

def notify(self, thread, comment):

body = self.format(thread, comment)

if "parent" in comment:
comment_parent = self.isso.db.comments.get(comment["parent"])
# Notify the author that a new comment is posted if requested
if comment_parent and "email" in comment_parent and comment_parent["notification"]:
body = self.format(thread, comment, admin=False)
subject = "Re: New comment posted on %s" % thread["title"]
self.sendmail(subject, body, thread, comment, to=comment_parent["email"])

body = self.format(thread, comment, admin=True)
self.sendmail(thread["title"], body, thread, comment)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thing for later: the email value is not actually verified to contain a valid email address. Hence it should be checked what kind of errors can appear when an invalid email address is used (e.g. the while loop for the uWSGI implementation).

def sendmail(self, subject, body, thread, comment, to=None):
if uwsgi:
uwsgi.spool({b"subject": thread["title"].encode("utf-8"),
b"body": body.encode("utf-8")})
uwsgi.spool({b"subject": subject.encode("utf-8"),
b"body": body.encode("utf-8"),
b"to": to})
else:
start_new_thread(self._retry, (thread["title"], body))
start_new_thread(self._retry, (subject, body, to))

def _sendmail(self, subject, body):
def _sendmail(self, subject, body, to=None):

from_addr = self.conf.get("from")
to_addr = self.conf.get("to")
to_addr = to or self.conf.get("to")

msg = MIMEText(body, 'plain', 'utf-8')
msg['From'] = "Ich schrei sonst! <%s>" % from_addr
Expand All @@ -131,10 +141,10 @@ def _sendmail(self, subject, body):
with self as con:
con.sendmail(from_addr, to_addr, msg.as_string())

def _retry(self, subject, body):
def _retry(self, subject, body, to):
for x in range(5):
try:
self._sendmail(subject, body)
self._sendmail(subject, body, to)
except smtplib.SMTPConnectError:
time.sleep(60)
else:
Expand Down
1 change: 1 addition & 0 deletions isso/js/app/i18n/en.js
Expand Up @@ -3,6 +3,7 @@ define({
"postbox-author": "Name (optional)",
"postbox-email": "E-mail (optional)",
"postbox-submit": "Submit",
"postbox-notification": "Subscribe to email notification of replies",

"num-comments": "One Comment\n{{ n }} Comments",
"no-comments": "No Comments Yet",
Expand Down
1 change: 1 addition & 0 deletions isso/js/app/i18n/fr.js
Expand Up @@ -3,6 +3,7 @@ define({
"postbox-author": "Nom (optionel)",
"postbox-email": "Courriel (optionel)",
"postbox-submit": "Soumettre",
"postbox-notification": "S'abonner aux notifications de réponses",

"num-comments": "Un commentaire\n{{ n }} commentaires",
"no-comments": "Aucun commentaire pour l'instant",
Expand Down
4 changes: 3 additions & 1 deletion isso/js/app/isso.js
Expand Up @@ -34,6 +34,7 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m
.then(function(rv) {
$(".avatar svg", el).replace(lib.identicons.generate(rv, 4, 48));
});
$(".notification-section").style.display = "block";
}, 200);
}, false);

Expand Down Expand Up @@ -63,7 +64,8 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m
author: $("[name=author]", el).value || null,
email: $("[name=email]", el).value || null,
text: $("textarea", el).value,
parent: parent || null
parent: parent || null,
notification: $("[name=notification]", el).checked ? 1 : 0,
}).then(function(comment) {
$("[name=author]", el).value = "";
$("[name=email]", el).value = "";
Expand Down
4 changes: 4 additions & 0 deletions isso/js/app/text/postbox.html
Expand Up @@ -6,6 +6,7 @@
<div class="textarea-wrapper">
<textarea name="text" rows="2" placeholder="{{ i18n-postbox-text }}"></textarea>
</div>

<section class="auth-section">
<p class="input-wrapper">
<input type="text" name="author" placeholder="{{ i18n-postbox-author }}"/>
Expand All @@ -17,5 +18,8 @@
<input type="submit" value="{{ i18n-postbox-submit }}"/>
</p>
</section>
<section class="notification-section">
<label><input type="checkbox" name="notification"/> {{ i18n-postbox-notification }}</label>
</section>
</div>
</div>
4 changes: 2 additions & 2 deletions isso/views/comments.py
Expand Up @@ -52,10 +52,10 @@ def dec(self, env, req, *args, **kwargs):
class API(object):

FIELDS = set(['id', 'parent', 'text', 'author', 'website', 'email',
'mode', 'created', 'modified', 'likes', 'dislikes', 'hash'])
'mode', 'created', 'modified', 'likes', 'dislikes', 'hash', 'notification'])

# comment fields, that can be submitted
ACCEPT = set(['text', 'author', 'website', 'email', 'parent'])
ACCEPT = set(['text', 'author', 'website', 'email', 'parent', 'notification'])

VIEWS = [
('fetch', ('GET', '/')),
Expand Down