Merge branch 'legacy/0.9'

Conflicts:
	CHANGES.rst
	docs/docs/configuration/client.rst
	setup.py
This commit is contained in:
Martin Zimmermann 2014-08-18 13:09:24 +02:00
commit 721e87a843
9 changed files with 93 additions and 52 deletions

View File

@ -7,12 +7,32 @@ Changelog for Isso
- Nothing changed yet. - Nothing changed yet.
0.9.6 (unreleased) 0.9.7 (unreleased)
------------------ ------------------
- Nothing changed yet. - Nothing changed yet.
0.9.6 (2014-08-18)
------------------
- remember name, email and website in localStorage, #119
- add option to hide voting feature, #115
data-isso-vote="true|false"
- remove email field from JSON responses
This is a quite serious issue. For the identicon, an expensive hash is used
to avoid the leakage of personal information like a real email address. A
`git blame` reveals, the email has been unintenionally exposed since the very
first release of Isso :-/
The testsuite now contains a dedicated test to prevent this error in the
future.
0.9.5 (2014-08-10) 0.9.5 (2014-08-10)
------------------ ------------------

View File

@ -16,6 +16,7 @@ preferably in the script tag which embeds the JS:
data-isso-avatar="true" data-isso-avatar="true"
data-isso-avatar-bg="#f0f0f0" data-isso-avatar-bg="#f0f0f0"
data-isso-avatar-fg="#9abf88 #5698c4 #e279a3 #9163b6 ..." data-isso-avatar-fg="#9abf88 #5698c4 #e279a3 #9163b6 ..."
data-isso-vote="true"
src="/prefix/js/embed.js"></script> src="/prefix/js/embed.js"></script>
Furthermore you can override the automatic title detection inside Furthermore you can override the automatic title detection inside
@ -88,6 +89,11 @@ scheme is based in `this color palette <http://colrd.com/palette/19308/>`_.
Multiple colors must be separated by space. If you use less than eight colors Multiple colors must be separated by space. If you use less than eight colors
and not a multiple of 2, the color distribution is not even. and not a multiple of 2, the color distribution is not even.
data-isso-vote
--------------
Enable or disable voting feature on the client side.
data-isso-id data-isso-id
------------ ------------

View File

@ -127,10 +127,11 @@
color: #111111 !important; color: #111111 !important;
text-shadow: #aaaaaa 0 0 1px !important; text-shadow: #aaaaaa 0 0 1px !important;
} }
.isso-comment > div.text-wrapper > .isso-comment-footer a.reply, .isso-comment > div.text-wrapper > .isso-comment-footer > a {
.isso-comment > div.text-wrapper > .isso-comment-footer a.edit, position: relative;
.isso-comment > div.text-wrapper > .isso-comment-footer a.cancel, top: .2em;
.isso-comment > div.text-wrapper > .isso-comment-footer a.delete { }
.isso-comment > div.text-wrapper > .isso-comment-footer > a + a {
padding-left: 1em; padding-left: 1em;
} }
.isso-comment > div.text-wrapper > .isso-comment-footer .votes { .isso-comment > div.text-wrapper > .isso-comment-footer .votes {

View File

@ -11,7 +11,8 @@ define(function() {
"avatar": true, "avatar": true,
"avatar-bg": "#f0f0f0", "avatar-bg": "#f0f0f0",
"avatar-fg": ["#9abf88", "#5698c4", "#e279a3", "#9163b6", "avatar-fg": ["#9abf88", "#5698c4", "#e279a3", "#9163b6",
"#be5168", "#f19670", "#e4bf80", "#447c69"].join(" ") "#be5168", "#f19670", "#e4bf80", "#447c69"].join(" "),
"vote": true
}; };
var js = document.getElementsByTagName("script"); var js = document.getElementsByTagName("script");

View File

@ -39,31 +39,31 @@ define(function() {
}); });
}; };
window.Element.prototype.toggle = function(type, on, off) { window.Element.prototype.toggle = function(type, a, b) {
/* /*
Toggle between two internal states on event :param type: e.g. to Toggle between two internal states on event :param type: e.g. to
cycle form visibility. Callback :param on: is called on first event, cycle form visibility. Callback :param a: is called on first event,
:param off: next time. :param b: next time.
You can skip to the next state without executing the callback with You can skip to the next state without executing the callback with
`toggler.next()`. You can prevent a cycle when you call `toggler.wait()` `toggler.next()`. You can prevent a cycle when you call `toggler.wait()`
during an event. during an event.
*/ */
function Toggle(el, on, off) { function Toggle(el, a, b) {
this.state = false; this.state = false;
this.el = el; this.el = el;
this.on = on; this.a = a;
this.off = off; this.b = b;
} }
Toggle.prototype.next = function next() { Toggle.prototype.next = function next() {
if (! this.state) { if (! this.state) {
this.state = true; this.state = true;
this.on(this); this.a(this);
} else { } else {
this.state = false; this.state = false;
this.off(this); this.b(this);
} }
}; };
@ -71,7 +71,7 @@ define(function() {
this.state = ! this.state; this.state = ! this.state;
}; };
var toggler = new Toggle(this, on, off); var toggler = new Toggle(this, a, b);
this.on(type, function() { this.on(type, function() {
toggler.next(); toggler.next();
}); });

View File

@ -7,7 +7,11 @@ define(["app/dom", "app/utils", "app/config", "app/api", "app/jade", "app/i18n",
var Postbox = function(parent) { var Postbox = function(parent) {
var el = $.htmlify(jade.render("postbox")); var el = $.htmlify(jade.render("postbox", {
"author": JSON.parse(localStorage.getItem("author")),
"email": JSON.parse(localStorage.getItem("email")),
"website": JSON.parse(localStorage.getItem("website"))
}));
// callback on success (e.g. to toggle the reply button) // callback on success (e.g. to toggle the reply button)
el.onsuccess = function() {}; el.onsuccess = function() {};
@ -29,16 +33,19 @@ define(["app/dom", "app/utils", "app/config", "app/api", "app/jade", "app/i18n",
return; return;
} }
var author = $("[name=author]", el).value || null,
email = $("[name=email]", el).value || null,
website = $("[name=website]", el).value || null;
localStorage.setItem("author", JSON.stringify(author));
localStorage.setItem("email", JSON.stringify(email));
localStorage.setItem("website", JSON.stringify(website));
api.create($("#isso-thread").getAttribute("data-isso-id"), { api.create($("#isso-thread").getAttribute("data-isso-id"), {
author: $("[name=author]", el).value || null, author: author, email: email, website: website,
email: $("[name=email]", el).value || null,
website: $("[name=website]", el).value || null,
text: utils.text($(".textarea", el).innerHTML), text: utils.text($(".textarea", el).innerHTML),
parent: parent || null parent: parent || null
}).then(function(comment) { }).then(function(comment) {
$("[name=author]", el).value = "";
$("[name=email]", el).value = "";
$("[name=website]", el).value = "";
$(".textarea", el).innerHTML = ""; $(".textarea", el).innerHTML = "";
$(".textarea", el).blur(); $(".textarea", el).blur();
insert(comment, true); insert(comment, true);
@ -144,31 +151,33 @@ define(["app/dom", "app/utils", "app/config", "app/api", "app/jade", "app/i18n",
} }
); );
// update vote counter, but hide if votes sum to 0 if (config.vote) {
var votes = function(value) { // update vote counter, but hide if votes sum to 0
var span = $("span.votes", footer); var votes = function (value) {
if (span === null && value !== 0) { var span = $("span.votes", footer);
footer.prepend($.new("span.votes", value)); if (span === null && value !== 0) {
} else { footer.prepend($.new("span.votes", value));
if (value === 0) {
span.remove();
} else { } else {
span.textContent = value; if (value === 0) {
span.remove();
} else {
span.textContent = value;
}
} }
} };
};
$("a.upvote", footer).on("click", function() { $("a.upvote", footer).on("click", function () {
api.like(comment.id).then(function(rv) { api.like(comment.id).then(function (rv) {
votes(rv.likes - rv.dislikes); votes(rv.likes - rv.dislikes);
});
}); });
});
$("a.downvote", footer).on("click", function() { $("a.downvote", footer).on("click", function () {
api.dislike(comment.id).then(function(rv) { api.dislike(comment.id).then(function (rv) {
votes(rv.likes - rv.dislikes); votes(rv.likes - rv.dislikes);
});
}); });
}); }
$("a.edit", footer).toggle("click", $("a.edit", footer).toggle("click",
function(toggler) { function(toggler) {

View File

@ -23,13 +23,14 @@ div(class='isso-comment' id='isso-#{comment.id}')
!= comment.text != comment.text
div(class='isso-comment-footer') div(class='isso-comment-footer')
if comment.likes - comment.dislikes != 0 if conf.vote
span(class='votes') #{comment.likes - comment.dislikes} if comment.likes - comment.dislikes != 0
a(class='upvote' href='#') span(class='votes') #{comment.likes - comment.dislikes}
i!= svg['arrow-up'] a(class='upvote' href='#')
span(class='spacer') | != svg['arrow-up']
a(class='downvote' href='#') span(class='spacer') |
i!= svg['arrow-down'] a(class='downvote' href='#')
!= svg['arrow-down']
a(class='reply' href='#') #{i18n('comment-reply')} a(class='reply' href='#') #{i18n('comment-reply')}
a(class='edit' href='#') #{i18n('comment-edit')} a(class='edit' href='#') #{i18n('comment-edit')}
a(class='delete' href='#') #{i18n('comment-delete')} a(class='delete' href='#') #{i18n('comment-delete')}

View File

@ -5,10 +5,13 @@ div(class='isso-postbox')
= i18n('postbox-text') = i18n('postbox-text')
section(class='auth-section') section(class='auth-section')
p(class='input-wrapper') p(class='input-wrapper')
input(type='text' name='author' placeholder=i18n('postbox-author')) input(type='text' name='author' placeholder=i18n('postbox-author')
value=author !== null ? '#{author}' : '')
p(class='input-wrapper') p(class='input-wrapper')
input(type='email' name='email' placeholder=i18n('postbox-email')) input(type='email' name='email' placeholder=i18n('postbox-email')
value=email != null ? '#{email}' : '')
p(class='input-wrapper') p(class='input-wrapper')
input(type='text' name='website' placeholder=i18n('postbox-website')) input(type='text' name='website' placeholder=i18n('postbox-website')
value=website != null ? '#{website}' : '')
p(class='post-action') p(class='post-action')
input(type='submit' value=i18n('postbox-submit')) input(type='submit' value=i18n('postbox-submit'))

View File

@ -71,7 +71,7 @@ def xhr(func):
class API(object): class API(object):
FIELDS = set(['id', 'parent', 'text', 'author', 'website', 'email', FIELDS = set(['id', 'parent', 'text', 'author', 'website',
'mode', 'created', 'modified', 'likes', 'dislikes', 'hash']) 'mode', 'created', 'modified', 'likes', 'dislikes', 'hash'])
# comment fields, that can be submitted # comment fields, that can be submitted