Merge remote-tracking branch 'tsileo/reply_notification' into reply_notification

Conflicts:
	isso/css/isso.scss
	isso/db/comments.py
	isso/ext/notifications.py
	isso/js/app/i18n/fr.js
	isso/js/app/isso.js
	isso/js/app/text/postbox.html
	isso/views/comments.py
pull/443/head
Pelle Nilsson 6 years ago
commit c30873e8a2

@ -209,6 +209,10 @@
.isso-postbox > .form-wrapper > .auth-section .post-action > input:active {
background-color: #BBB;
}
.isso-postbox > .form-wrapper > .notification-section {
display: none;
padding-bottom: 10px;
}
@media screen and (max-width:600px) {
.isso-postbox > .form-wrapper > .auth-section .input-wrapper {
display: block;

@ -23,7 +23,7 @@ class Comments:
'mode', # status of the comment 1 = valid, 2 = pending,
# 4 = soft-deleted (cannot hard delete because of replies)
'remote_addr', 'text', 'author', 'email', 'website',
'likes', 'dislikes', 'voters']
'likes', 'dislikes', 'voters', 'notification']
def __init__(self, db):
@ -33,7 +33,8 @@ class Comments:
' 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 NOT NULL DEFAULT 0);'])
def add(self, uri, c):
"""
@ -50,16 +51,16 @@ class Comments:
'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'),
uri)
)

@ -88,7 +88,7 @@ class SMTP(object):
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()
@ -101,40 +101,51 @@ class SMTP(object):
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"]))
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)
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'] = from_addr
@ -145,10 +156,10 @@ class SMTP(object):
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:

@ -90,6 +90,8 @@ define(function() {
this.focus = function() { node.focus() };
this.scrollIntoView = function(args) { node.scrollIntoView(args) };
this.checked = function() { return node.checked; };
this.setAttribute = function(key, value) { node.setAttribute(key, value) };
this.getAttribute = function(key) { return node.getAttribute(key) };

@ -4,6 +4,7 @@ define({
"postbox-email": "E-mail (optional)",
"postbox-website": "Website (optional)",
"postbox-submit": "Submit",
"postbox-notification": "Subscribe to email notification of replies",
"num-comments": "One Comment\n{{ n }} Comments",
"no-comments": "No Comments Yet",

@ -4,6 +4,7 @@ define({
"postbox-email": "Courriel (optionnel)",
"postbox-website": "Site web (optionnel)",
"postbox-submit": "Soumettre",
"postbox-notification": "S'abonner aux notifications de réponses",
"num-comments": "{{ n }} commentaire\n{{ n }} commentaires",
"no-comments": "Aucun commentaire pour l'instant",
"comment-reply": "Répondre",

@ -39,6 +39,17 @@ define(["app/dom", "app/utils", "app/config", "app/api", "app/jade", "app/i18n",
return true;
};
// only display notification checkbox if email is filled in
var email_edit = function() {
if ($("[name='email']", el).value.length > 0) {
$(".notification-section", el).show();
} else {
$(".notification-section", el).hide();
}
};
$("[name='email']", el).on("input", email_edit);
email_edit();
// email is not optional if this config parameter is set
if (config["require-email"]) {
$("[name='email']", el).setAttribute("placeholder",
@ -70,7 +81,8 @@ define(["app/dom", "app/utils", "app/config", "app/api", "app/jade", "app/i18n",
author: author, email: email, website: website,
text: utils.text($(".textarea", el).innerHTML),
parent: parent || null,
title: $("#isso-thread").getAttribute("data-title") || null
title: $("#isso-thread").getAttribute("data-title") || null,
notification: $("[name=notification]", el).checked() ? 1 : 0,
}).then(function(comment) {
$(".textarea", el).innerHTML = "";
$(".textarea", el).blur();

@ -15,3 +15,7 @@ div(class='isso-postbox')
value=website != null ? '#{website}' : '')
p(class='post-action')
input(type='submit' value=i18n('postbox-submit'))
section(class='notification-section')
label
input(type='checkbox' name='notification')
= i18n('postbox-notification')

@ -81,10 +81,10 @@ def xhr(func):
class API(object):
FIELDS = set(['id', 'parent', 'text', 'author', 'website',
'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', 'title'])
ACCEPT = set(['text', 'author', 'website', 'email', 'parent', 'title', 'notification'])
VIEWS = [
('fetch', ('GET', '/')),

Loading…
Cancel
Save