commit work in progress

completely revamp JS client because JS sucks^W^W^W to feature AMD,
require.js, promises and HTML.js.

The comment listing is now more like Disqus and for now comment
retrieval, comment creation and deletion works. Form validation is
rudimentary implemented as well.

replaced Mako with Jinja2 (because... I forgot.), admin interface will
use Bootstrap™ but is not functional yet.

features a progress indicator in case you're sqlite db performs *really*
bad
pull/16/head
Martin Zimmermann 11 years ago
parent 7e6fa0438b
commit f6271e5cf6

@ -155,7 +155,8 @@ def main():
sys.exit(0)
app = SharedDataMiddleware(isso.wsgi_app, {
'/static': join(dirname(__file__), 'static/')
'/static': join(dirname(__file__), 'static/'),
'/client': join(dirname(__file__), 'client/')
})
run_simple(conf.get('server', 'host'), conf.getint('server', 'port'),

@ -0,0 +1,111 @@
/*
* Copyright 2013, Martin Zimmermann <info@posativ.org>. All rights reserved.
* License: BSD Style, 2 clauses. See isso/__init__.py.
*/
define(["lib/q", "app/models"], function(Q, models) {
// http://stackoverflow.com/questions/17544965/unhandled-rejection-reasons-should-be-empty
Q.stopUnhandledRejectionTracking();
Q.longStackSupport = true;
var endpoint = null,
location = window.location.pathname;
// guess Isso API location
var js = document.getElementsByTagName("script");
for (var i = 0; i < js.length; i++) {
if (js[i].src.match("/client/require\\.js$")) {
endpoint = js[i].src.substring(0, js[i].src.length - 18);
break;
}
throw "no Isso API location found";
}
var curl = function(method, url, data) {
var request = new XMLHttpRequest();
var response = Q.defer();
function onload() {
response.resolve({status: request.status, body: request.responseText});
}
try {
request.open(method, url, true);
request.overrideMimeType("application/javascript");
request.onreadystatechange = function () {
if (request.readyState === 4) {
onload();
}
};
} catch (exception) {
response.reject(exception.message);
}
request.send(data);
return response.promise;
};
var qs = function(params) {
rv = "";
for (var key in params) {
if (params.hasOwnProperty(key)) {
rv += key + "=" + encodeURIComponent(params[key]) + "&";
}
}
return rv.substring(0, rv.length - 1) // chop off trailing "&"
}
var create = function(data) {
return curl("POST", endpoint + "/new?" + qs({uri: location}), JSON.stringify(data))
.then(function (rv) {
if (rv.status == 201 || rv.status == 202) {
return JSON.parse(rv.body);
} else {
msg = rv.body.match("<p>(.+)</p>")
throw {status: rv.status, reason: (msg && msg[1]) || rv.body}
}
})
}
var modify = function(data) {
// ...
}
var remove = function(id) {
return curl("DELETE", endpoint + "/?" + qs({uri: location, id: id}), null)
.then(function(rv) {
if (rv.status == 200) {
return rv;
} else {
throw {status: rv.status, reason: rv.body}
}
})
}
var fetchall = function() {
return curl("GET", endpoint + "/?" + qs({uri: location}), null)
.then(function (rv) {
if (rv.status == 200) {
return JSON.parse(rv.body)
} else {
msg = rv.body.match("<p>(.+)</p>")
throw {status: rv.status, reason: (msg && msg[1]) || rv.body}
}
})
}
return {
endpoint: endpoint,
create: create,
remove: remove,
fetchall: fetchall
}
});

@ -0,0 +1,54 @@
define(["lib/HTML", "./logging"], function(HTML, logging) {
var msgbox = function(defaults) {
var form = document.createElement("div")
form.className = "isso-comment-box"
HTML.ify(form);
var optional = form.add("ul.optional");
optional.add("li>input[type=text name=author placeholder=Name ]").value = defaults.author || "";
optional.add("li>input[type=email name=email placeholder=Email]").value = defaults.email || "";
optional.add("li>input[type=url name=website placeholder=Website]").value = defaults.website || "";
var textarea = form.add("div>textarea[rows=2 name=text]");
textarea.value = defaults.text || "";
textarea.placeholder = "Kommentar hier eintippen (andere Felder sind optional)"
textarea.onfocus = function() {
textarea.rows = 10
};
textarea.onblur = function() { setTimeout(function() {
if (textarea.value == "" && document.activeElement != textarea) {
textarea.rows = 2
}}, 500)};
form.add("input[type=submit]").value = "Kommentar hinzufügen";
form.add("span");
return form;
}
var validate = function(msgbox) {
if (msgbox.query("textarea").value.length < 3) {
msgbox.query("textarea").focus();
msgbox.span.className = "isso-popup"
msgbox.span.innerHTML = "Dein Kommentar sollte schon etwas länger sein.";
msgbox.span.addEventListener("click", function(event) {
msgbox.span.className = "";
msgbox.span.innerHTML = "";
})
setTimeout(function() {
msgbox.span.className = ""
msgbox.span.innerHTML = ""
}, 5000 )
return false;
}
return true;
}
return {
msgbox: msgbox,
validate: validate
}
});

@ -0,0 +1,202 @@
/* Isso Ich schrei sonst!
*
* Copyright 2013, Martin Zimmermann <info@posativ.org>. All rights reserved.
* License: BSD Style, 2 clauses. See isso/__init__.py.
*/
define(["lib/q", "lib/HTML", "helper/utils", "./api", "./forms", "./logging"], function(Q, HTML, utils, api, forms, logging) {
var defaults = {
text: "Lorem ipsum ...", author: "Anonymous",
email: "info@example.org", website: "..."
};
var insert = function(comment) {
/*
* insert a comment (JSON/object) into the #isso-thread or below a parent (#isso-N), renders some HTML and
* registers events to reply to, edit and remove a comment.
*/
if (comment.parent) {
entrypoint = HTML.query("#isso-" + comment.parent).add("div.isso-follow-up");
} else {
entrypoint = HTML.query("div#isso-root")
}
entrypoint.add("article.isso-comment#isso-" + comment.id)
.add("header+span.avatar+div.text+footer")
var node = HTML.query("#isso-" + comment.id),
date = new Date(parseInt(comment.created) * 1000);
if (comment.mode == 2) {
node.header.add("span.note").textContent = 'Kommentar muss noch freigeschaltet werden';
} else if (comment.mode == 4) { // deleted
node.classList.add('deleted');
node.header.add("span.note").textContent = "Kommentar gelöscht."
}
if (comment.website) {
var el = node.header.add("a.author")
el.textContent= comment.author || 'Anonymous';
el.href = comment.website;
el.rel = "nofollow"
} else {
node.header.add("span.author").innerHTML = comment.author || 'Anonymous';
}
node.header.add("span.spacer").textContent = "•";
var permalink = node.header.add("a.permalink");
permalink.href = '#isso-' + comment.id;
permalink.add("date[datetime=" + date.getUTCFullYear() + "-" + date.getUTCMonth() + "-" + date.getUTCDay() + "]")
.textContent = utils.ago(date);
node.query("span.avatar").add("img[width=48 height=48]");
if (comment.mode == 4) {
node.query(".text").add("p").value = "&nbsp;"
} else {
node.query(".text").innerHTML = comment.text;
}
node.footer.add("a.liek{Liek}").href = "#";
node.footer.add("a.reply{Antworten}").href = "#";
if (utils.read(window.location.pathname + "-" + comment.id)) {
node.footer.add("a.delete{Löschen}").href = "#";
node.footer.add("a.edit{Bearbeiten}").href = "#";
var delbtn = node.query("a.delete"),
editbtn = node.query("a.edit");
delbtn.addEventListener("click", function(event) {
if (delbtn.textContent == "Bestätigen") {
api.remove(comment.id).then(function(rv) {
console.log(rv);
})
} else {
delbtn.textContent = "Bestätigen"
setTimeout(function() {delbtn.textContent = "Löschen"}, 1500)
}
event.preventDefault();
})
}
// if (read(path + '-' + post['id'])) {
// $('#isso_' + post['id'] + '> footer > a:first-child')
// .after(brew(['a', {'class': 'delete', 'href': '#'}, 'Löschen']))
// .after(brew(['a', {'class': 'edit', 'href': '#'}, 'Bearbeiten']));
//
// // DELETE
// $('#isso_' + post['id'] + ' > footer .delete').on('click', function(event) {
// isso.remove(post['id'], function(status, rv) {
// // XXX comment might not actually deleted
// $('#isso_' + post['id']).remove();
// });
// event.stop();
// });
//
// // EDIT
// $('#isso_' + post['id'] + ' > footer .edit').on('click', function(event) {
//
// if ($('#issoform_' + post['id']).length == 0) { // HTML form not shown
// isso.plain(post['id'], function(status, rv) {
// if (status != 200) return alert('Mööp');
// var rv = form(post['id'], JSON.parse(rv), function(form, id) {
// isso.modify(id, extract(form, post['parent']), function(status, rv) {
// if (status != 200) return alert('Mööp');
//
// $('#issoform_' + post['id']).remove();
// $('#isso_' + post['id']).remove();
// insert(JSON.parse(rv));
// });
// });
//
// $('#isso_' + post['id']).after(rv);
// $('input[type="submit"]', rv)[0].value = 'Bestätigen.';
// });
// } else {
// $('#issoform_' + post['id']).remove();
// };
// event.stop();
// });
// };
// ability to answer directly to a comment
HTML.query("#isso-" + comment.id + " a.reply").addEventListener("click", function(event) {
// remove active form when clicked again or reply to another comment
var active = HTML.query(".isso-active-msgbox"); // [] when empty, element if not
if (! (active instanceof Array)) {
active.query("div.isso-comment-box").remove()
active.classList.remove("isso-active-msgbox");
active.query("a.reply").textContent = "Antworten"
if (active.id == "isso-" + comment.id) {
event.preventDefault();
return;
}
}
var msgbox = forms.msgbox({})
HTML.query("#isso-" + comment.id).footer.appendChild(msgbox)
HTML.query("#isso-" + comment.id).classList.add("isso-active-msgbox");
HTML.query("#isso-" + comment.id + " a.reply").textContent = "Schließen";
msgbox.query("input[type=submit]").addEventListener("click", function(event) {
forms.validate(msgbox) && api.create({
author: msgbox.query("[name=author]").value,
email: msgbox.query("[name=email]").value,
website: msgbox.query("[name=website]").value,
text: msgbox.query("textarea").value,
parent: comment.id })
.then(function(rv) {
// remove box on submit
msgbox.parentNode.parentNode.classList.remove("isso-active-msgbox");
msgbox.parentNode.parentNode.query("a.reply").textContent = "Antworten"
msgbox.remove()
insert(rv);
})
event.preventDefault()
});
event.preventDefault();
});
}
var init = function() {
console.log(utils.heading());
// return;
var rootmsgbox = forms.msgbox({});
HTML.query("#isso-thread").add("div#isso-root").add(rootmsgbox);
rootmsgbox.query("input[type=submit]").addEventListener("click", function(event) {
forms.validate(rootmsgbox) && api.create({
author: rootmsgbox.query("[name=author]").value,
email: rootmsgbox.query("[name=email]").value,
website: rootmsgbox.query("[name=website]").value,
text: rootmsgbox.query("textarea").value,
parent: null })
.then(function(rv) {
// remove box on submit
rootmsgbox.remove()
insert(rv);
})
event.preventDefault()
});
api.fetchall()
.then(function(comments) {
for (var i in comments) {
insert(comments[i])
}})
.fail(logging.error)
}
return {
init: init
}
});

@ -0,0 +1,9 @@
define({
error: function(err) {
if ("status" in err && "reason" in err) {
console.error("%i: %s", err.status, err.reason)
} else {
console.error(err.stack)
}
}
})

@ -0,0 +1,10 @@
define(function() {
function Comment(data) {
this.text = data["text"];
}
return {
Comment: Comment
}
});

@ -0,0 +1,5 @@
require(["lib/ready", "app/isso"], function(domready, isso) {
domready(function() {
isso.init();
})
});

@ -0,0 +1,81 @@
/* Copyright 2013, Martin Zimmermann <info@posativ.org>. All rights reserved.
* License: BSD Style, 2 clauses. See isso/__init__.py.
*
* utility functions
*/
define({
// return `cookie` string if set
read: function(cookie) {
return (document.cookie.match('(^|; )' + cookie + '=([^;]*)') || 0)[2]
},
ago: function(date) {
/*!
* JavaScript Pretty Date
* Copyright (c) 2011 John Resig (ejohn.org)
* Licensed under the MIT and GPL licenses.
*/
var diff = (((new Date()).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31)
return;
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && "vor " + Math.floor(diff / 60) + " Minuten" ||
diff < 7200 && "vor einer Stunde" ||
diff < 86400 && "vor " + Math.floor(diff / 3600) + " Stunden") ||
day_diff == 1 && "Gestern" ||
day_diff < 7 && "vor " + day_diff + " Tagen" ||
day_diff < 31 && "vor " + Math.ceil(day_diff / 7) + " Wochen";
},
heading: function() {
/*
* return first level heading that is probably the
* blog title. If no h1 is found, "Untitled." is used.
*/
var el = document.getElementById("isso-thread");
var visited = [];
var recurse = function(node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
if (child.nodeType != child.ELEMENT_NODE) {
continue;
}
if (child.nodeName == "H1") {
return child;
}
if (visited.indexOf(child) == -1) {
return recurse(child);
}
}
}
while (true) {
visited.push(el);
if (el == document.documentElement) {
break
}
var rv = recurse(el);
if (rv) {
return rv.textContent
}
el = el.parentNode;
}
return "Untitled."
}
});

@ -1,41 +1,35 @@
/* Isso Ich schrei sonst!
*
* Copyright 2012, Martin Zimmermann <info@posativ.org>. All rights reserved.
* Copyright 2013, Martin Zimmermann <info@posativ.org>. All rights reserved.
* License: BSD Style, 2 clauses. See isso/__init__.py.
*
*
* Code requires Bean, Bonzo, Qwery, domReady (all are part of jeesh) and
* reqwest. To ease integration with websites, all classes are prefixed
* with `isso`.
*/
// Uhm. Namespaces are one honking great idea, aren't they?
var isso = isso || {},
prefix = "",
path = encodeURIComponent(window.location.pathname);
var isso = {};
// XXX
isso.prefix = prefix;
isso.path = path;
var init = function() {
var isso = new Object();
/*
* isso specific helpers to create, modify, remove and receive comments
*/
// guess Isso API location
var js = document.getElementsByTagName("script");
for (var i = 0; i < js.length; i++) {
if (js[i].src.match("/client/require\\.js$")) {
isso.location = js[i].src.substring(0, 18);
break;
}
}
function verify(data) {
return data['text'] == null ? false : true
};
console.log(isso.location)
}
isso.create = function(data, func) {
if (!verify(data)) {
return;
}
var request = new XMLHttpRequest();
$.ajax('POST', prefix + '/1.0/' + isso.path + '/new',
JSON.stringify(data), {'Content-Type': 'application/json'}).then(func);
// $.ajax('POST', prefix + '/1.0/' + isso.path + '/new',
// JSON.stringify(data), {'Content-Type': 'application/json'}).then(func);
};

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -0,0 +1,55 @@
/*!
* domready (c) Dustin Diaz 2012 - License MIT
*/
!function (name, definition) {
if (typeof module != 'undefined') module.exports = definition()
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition)
else this[name] = definition()
}('domready', function (ready) {
var fns = [], fn, f = false
, doc = document
, testEl = doc.documentElement
, hack = testEl.doScroll
, domContentLoaded = 'DOMContentLoaded'
, addEventListener = 'addEventListener'
, onreadystatechange = 'onreadystatechange'
, readyState = 'readyState'
, loadedRgx = hack ? /^loaded|^c/ : /^loaded|c/
, loaded = loadedRgx.test(doc[readyState])
function flush(f) {
loaded = 1
while (f = fns.shift()) f()
}
doc[addEventListener] && doc[addEventListener](domContentLoaded, fn = function () {
doc.removeEventListener(domContentLoaded, fn, f)
flush()
}, f)
hack && doc.attachEvent(onreadystatechange, fn = function () {
if (/^c/.test(doc[readyState])) {
doc.detachEvent(onreadystatechange, fn)
flush()
}
})
return (ready = hack ?
function (fn) {
self != top ?
loaded ? fn() : fns.push(fn) :
function () {
try {
testEl.doScroll('left')
} catch (e) {
return setTimeout(function() { ready(fn) }, 50)
}
fn()
}()
} :
function (fn) {
loaded ? fn() : fns.push(fn)
})
})

@ -0,0 +1,6 @@
require(["minified"], function(minified) {
console.log(minified)
minified.$.ready(function() {
console.log(123);
})
});

@ -0,0 +1,36 @@
/*
RequireJS 2.1.8 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
Available via the MIT or new BSD license.
see: http://github.com/jrburke/requirejs for details
*/
var requirejs,require,define;
(function(Z){function H(b){return"[object Function]"===L.call(b)}function I(b){return"[object Array]"===L.call(b)}function y(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function M(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function s(b,c){return ga.call(b,c)}function l(b,c){return s(b,c)&&b[c]}function F(b,c){for(var d in b)if(s(b,d)&&c(b[d],d))break}function Q(b,c,d,h){c&&F(c,function(c,j){if(d||!s(b,j))h&&"string"!==typeof c?(b[j]||(b[j]={}),Q(b[j],
c,d,h)):b[j]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function aa(b){throw b;}function ba(b){if(!b)return b;var c=Z;y(b.split("."),function(b){c=c[b]});return c}function A(b,c,d,h){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=h;d&&(c.originalError=d);return c}function ha(b){function c(a,f,b){var e,m,c,g,d,h,j,i=f&&f.split("/");e=i;var n=k.map,p=n&&n["*"];if(a&&"."===a.charAt(0))if(f){e=l(k.pkgs,f)?i=[f]:i.slice(0,i.length-
1);f=a=e.concat(a.split("/"));for(e=0;f[e];e+=1)if(m=f[e],"."===m)f.splice(e,1),e-=1;else if(".."===m)if(1===e&&(".."===f[2]||".."===f[0]))break;else 0<e&&(f.splice(e-1,2),e-=2);e=l(k.pkgs,f=a[0]);a=a.join("/");e&&a===f+"/"+e.main&&(a=f)}else 0===a.indexOf("./")&&(a=a.substring(2));if(b&&n&&(i||p)){f=a.split("/");for(e=f.length;0<e;e-=1){c=f.slice(0,e).join("/");if(i)for(m=i.length;0<m;m-=1)if(b=l(n,i.slice(0,m).join("/")))if(b=l(b,c)){g=b;d=e;break}if(g)break;!h&&(p&&l(p,c))&&(h=l(p,c),j=e)}!g&&
h&&(g=h,d=j);g&&(f.splice(0,d,g),a=f.join("/"))}return a}function d(a){z&&y(document.getElementsByTagName("script"),function(f){if(f.getAttribute("data-requiremodule")===a&&f.getAttribute("data-requirecontext")===i.contextName)return f.parentNode.removeChild(f),!0})}function h(a){var f=l(k.paths,a);if(f&&I(f)&&1<f.length)return d(a),f.shift(),i.require.undef(a),i.require([a]),!0}function $(a){var f,b=a?a.indexOf("!"):-1;-1<b&&(f=a.substring(0,b),a=a.substring(b+1,a.length));return[f,a]}function n(a,
f,b,e){var m,B,g=null,d=f?f.name:null,h=a,j=!0,k="";a||(j=!1,a="_@r"+(L+=1));a=$(a);g=a[0];a=a[1];g&&(g=c(g,d,e),B=l(r,g));a&&(g?k=B&&B.normalize?B.normalize(a,function(a){return c(a,d,e)}):c(a,d,e):(k=c(a,d,e),a=$(k),g=a[0],k=a[1],b=!0,m=i.nameToUrl(k)));b=g&&!B&&!b?"_unnormalized"+(M+=1):"";return{prefix:g,name:k,parentMap:f,unnormalized:!!b,url:m,originalName:h,isDefine:j,id:(g?g+"!"+k:k)+b}}function q(a){var f=a.id,b=l(p,f);b||(b=p[f]=new i.Module(a));return b}function t(a,f,b){var e=a.id,m=l(p,
e);if(s(r,e)&&(!m||m.defineEmitComplete))"defined"===f&&b(r[e]);else if(m=q(a),m.error&&"error"===f)b(m.error);else m.on(f,b)}function v(a,f){var b=a.requireModules,e=!1;if(f)f(a);else if(y(b,function(f){if(f=l(p,f))f.error=a,f.events.error&&(e=!0,f.emit("error",a))}),!e)j.onError(a)}function w(){R.length&&(ia.apply(G,[G.length-1,0].concat(R)),R=[])}function x(a){delete p[a];delete T[a]}function E(a,f,b){var e=a.map.id;a.error?a.emit("error",a.error):(f[e]=!0,y(a.depMaps,function(e,c){var g=e.id,
d=l(p,g);d&&(!a.depMatched[c]&&!b[g])&&(l(f,g)?(a.defineDep(c,r[g]),a.check()):E(d,f,b))}),b[e]=!0)}function C(){var a,f,b,e,m=(b=1E3*k.waitSeconds)&&i.startTime+b<(new Date).getTime(),c=[],g=[],j=!1,l=!0;if(!U){U=!0;F(T,function(b){a=b.map;f=a.id;if(b.enabled&&(a.isDefine||g.push(b),!b.error))if(!b.inited&&m)h(f)?j=e=!0:(c.push(f),d(f));else if(!b.inited&&(b.fetched&&a.isDefine)&&(j=!0,!a.prefix))return l=!1});if(m&&c.length)return b=A("timeout","Load timeout for modules: "+c,null,c),b.contextName=
i.contextName,v(b);l&&y(g,function(a){E(a,{},{})});if((!m||e)&&j)if((z||da)&&!V)V=setTimeout(function(){V=0;C()},50);U=!1}}function D(a){s(r,a[0])||q(n(a[0],null,!0)).init(a[1],a[2])}function J(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!W?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||W)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function K(){var a;for(w();G.length;){a=
G.shift();if(null===a[0])return v(A("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));D(a)}}var U,X,i,N,V,k={waitSeconds:7,baseUrl:"./",paths:{},pkgs:{},shim:{},config:{}},p={},T={},Y={},G=[],r={},S={},L=1,M=1;N={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module?a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){var b=
l(k.pkgs,a.map.id);return(b?l(k.config,a.map.id+"/"+b.main):l(k.config,a.map.id))||{}},exports:r[a.map.id]}}};X=function(a){this.events=l(Y,a.id)||{};this.map=a;this.shim=l(k.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};X.prototype={init:function(a,b,c,e){e=e||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=!0;
this.ignore=e.ignore;e.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var e=this.exports,m=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(H(m)){if(this.events.error&&this.map.isDefine||j.onError!==aa)try{e=i.execCb(c,m,b,e)}catch(d){a=d}else e=i.execCb(c,m,b,e);this.map.isDefine&&((b=this.module)&&void 0!==b.exports&&b.exports!==
this.exports?e=b.exports:void 0===e&&this.usingExports&&(e=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",v(this.error=a)}else e=m;this.exports=e;if(this.map.isDefine&&!this.ignore&&(r[c]=e,j.onResourceLoad))j.onResourceLoad(i,this.map,this.depMaps);x(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=
!0)}}else this.fetch()}},callPlugin:function(){var a=this.map,b=a.id,d=n(a.prefix);this.depMaps.push(d);t(d,"defined",u(this,function(e){var m,d;d=this.map.name;var g=this.map.parentMap?this.map.parentMap.name:null,h=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(e.normalize&&(d=e.normalize(d,function(a){return c(a,g,!0)})||""),e=n(a.prefix+"!"+d,this.map.parentMap),t(e,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),
d=l(p,e.id)){this.depMaps.push(e);if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else m=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),m.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];F(p,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&x(a.map.id)});v(a)}),m.fromText=u(this,function(e,c){var d=a.name,g=n(d),B=O;c&&(e=c);B&&(O=!1);q(g);s(k.config,b)&&(k.config[d]=k.config[b]);try{j.exec(e)}catch(ca){return v(A("fromtexteval",
"fromText eval for "+b+" failed: "+ca,ca,[b]))}B&&(O=!0);this.depMaps.push(g);i.completeLoad(d);h([d],m)}),e.load(a.name,h,m,k)}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){T[this.map.id]=this;this.enabling=this.enabled=!0;y(this.depMaps,u(this,function(a,b){var c,e;if("string"===typeof a){a=n(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=l(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;t(a,"defined",u(this,function(a){this.defineDep(b,
a);this.check()}));this.errback&&t(a,"error",u(this,this.errback))}c=a.id;e=p[c];!s(N,c)&&(e&&!e.enabled)&&i.enable(a,this)}));F(this.pluginMaps,u(this,function(a){var b=l(p,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){y(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:k,contextName:b,registry:p,defined:r,urlFetched:S,defQueue:G,Module:X,makeModuleMap:n,
nextTick:j.nextTick,onError:v,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=k.pkgs,c=k.shim,e={paths:!0,config:!0,map:!0};F(a,function(a,b){e[b]?"map"===b?(k.map||(k.map={}),Q(k[b],a,!0,!0)):Q(k[b],a,!0):k[b]=a});a.shim&&(F(a.shim,function(a,b){I(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);c[b]=a}),k.shim=c);a.packages&&(y(a.packages,function(a){a="string"===typeof a?{name:a}:a;b[a.name]={name:a.name,
location:a.location||a.name,main:(a.main||"main").replace(ja,"").replace(ea,"")}}),k.pkgs=b);F(p,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=n(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(Z,arguments));return b||a.exports&&ba(a.exports)}},makeRequire:function(a,f){function d(e,c,h){var g,k;f.enableBuildCallback&&(c&&H(c))&&(c.__requireJsBuild=!0);if("string"===typeof e){if(H(c))return v(A("requireargs",
"Invalid require call"),h);if(a&&s(N,e))return N[e](p[a.id]);if(j.get)return j.get(i,e,a,d);g=n(e,a,!1,!0);g=g.id;return!s(r,g)?v(A("notloaded",'Module name "'+g+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[g]}K();i.nextTick(function(){K();k=q(n(null,a));k.skipMap=f.skipMap;k.init(e,c,h,{enabled:!0});C()});return d}f=f||{};Q(d,{isBrowser:z,toUrl:function(b){var d,f=b.lastIndexOf("."),g=b.split("/")[0];if(-1!==f&&(!("."===g||".."===g)||1<f))d=b.substring(f,b.length),b=
b.substring(0,f);return i.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return s(r,n(b,a,!1,!0).id)},specified:function(b){b=n(b,a,!1,!0).id;return s(r,b)||s(p,b)}});a||(d.undef=function(b){w();var c=n(b,a,!0),f=l(p,b);delete r[b];delete S[c.url];delete Y[b];f&&(f.events.defined&&(Y[b]=f.events),x(b))});return d},enable:function(a){l(p,a.id)&&q(a).enable()},completeLoad:function(a){var b,c,e=l(k.shim,a)||{},d=e.exports;for(w();G.length;){c=G.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===
a&&(b=!0);D(c)}c=l(p,a);if(!b&&!s(r,a)&&c&&!c.inited){if(k.enforceDefine&&(!d||!ba(d)))return h(a)?void 0:v(A("nodefine","No define call for "+a,null,[a]));D([a,e.deps||[],e.exportsFn])}C()},nameToUrl:function(a,b,c){var e,d,h,g,i,n;if(j.jsExtRegExp.test(a))g=a+(b||"");else{e=k.paths;d=k.pkgs;g=a.split("/");for(i=g.length;0<i;i-=1)if(n=g.slice(0,i).join("/"),h=l(d,n),n=l(e,n)){I(n)&&(n=n[0]);g.splice(0,i,n);break}else if(h){a=a===h.name?h.location+"/"+h.main:h.location;g.splice(0,i,a);break}g=g.join("/");
g+=b||(/\?/.test(g)||c?"":".js");g=("/"===g.charAt(0)||g.match(/^[\w\+\.\-]+:/)?"":k.baseUrl)+g}return k.urlArgs?g+((-1===g.indexOf("?")?"?":"&")+k.urlArgs):g},load:function(a,b){j.load(i,a,b)},execCb:function(a,b,c,e){return b.apply(e,c)},onScriptLoad:function(a){if("load"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=J(a),i.completeLoad(a.id)},onScriptError:function(a){var b=J(a);if(!h(b.id))return v(A("scripterror","Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();
return i}var j,w,x,C,J,D,P,K,q,fa,la=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ma=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,ea=/\.js$/,ja=/^\.\//;w=Object.prototype;var L=w.toString,ga=w.hasOwnProperty,ia=Array.prototype.splice,z=!!("undefined"!==typeof window&&navigator&&window.document),da=!z&&"undefined"!==typeof importScripts,ka=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,W="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),E={},t={},R=[],O=
!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(H(requirejs))return;t=requirejs;requirejs=void 0}"undefined"!==typeof require&&!H(require)&&(t=require,require=void 0);j=requirejs=function(b,c,d,h){var q,n="_";!I(b)&&"string"!==typeof b&&(q=b,I(c)?(b=c,c=d,d=h):b=[]);q&&q.context&&(n=q.context);(h=l(E,n))||(h=E[n]=j.s.newContext(n));q&&h.configure(q);return h.require(b,c,d)};j.config=function(b){return j(b)};j.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,
4)}:function(b){b()};require||(require=j);j.version="2.1.8";j.jsExtRegExp=/^\/|:|\?|\.js$/;j.isBrowser=z;w=j.s={contexts:E,newContext:ha};j({});y(["toUrl","undef","defined","specified"],function(b){j[b]=function(){var c=E._;return c.require[b].apply(c,arguments)}});if(z&&(x=w.head=document.getElementsByTagName("head")[0],C=document.getElementsByTagName("base")[0]))x=w.head=C.parentNode;j.onError=aa;j.createNode=function(b){var c=b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):
document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};j.load=function(b,c,d){var h=b&&b.config||{};if(z)return h=j.createNode(h,c,d),h.setAttribute("data-requirecontext",b.contextName),h.setAttribute("data-requiremodule",c),h.attachEvent&&!(h.attachEvent.toString&&0>h.attachEvent.toString().indexOf("[native code"))&&!W?(O=!0,h.attachEvent("onreadystatechange",b.onScriptLoad)):(h.addEventListener("load",b.onScriptLoad,!1),h.addEventListener("error",
b.onScriptError,!1)),h.src=d,K=h,C?x.insertBefore(h,C):x.appendChild(h),K=null,h;if(da)try{importScripts(d),b.completeLoad(c)}catch(l){b.onError(A("importscripts","importScripts failed for "+c+" at "+d,l,[c]))}};z&&M(document.getElementsByTagName("script"),function(b){x||(x=b.parentNode);if(J=b.getAttribute("data-main"))return q=J,t.baseUrl||(D=q.split("/"),q=D.pop(),fa=D.length?D.join("/")+"/":"./",t.baseUrl=fa),q=q.replace(ea,""),j.jsExtRegExp.test(q)&&(q=J),t.deps=t.deps?t.deps.concat(q):[q],!0});
define=function(b,c,d){var h,j;"string"!==typeof b&&(d=c,c=b,b=null);I(c)||(d=c,c=null);!c&&H(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(h=K))P&&"interactive"===P.readyState||M(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),h=P;h&&(b||(b=h.getAttribute("data-requiremodule")),j=E[h.getAttribute("data-requirecontext")])}(j?j.defQueue:
R).push([b,c,d])};define.amd={jQuery:!0};j.exec=function(b){return eval(b)};j(t)}})(this);

@ -1,65 +0,0 @@
/* Copyright 2012, Martin Zimmermann <info@posativ.org>. All rights reserved.
* License: BSD Style, 2 clauses. See isso/__init__.py.
*
* utility functions -- JS Y U SO STUPID?
*
* read(cookie): return `cookie` string if set
* format(date): human-readable date formatting
* brew(array): similar to DOMinate essentials
*/
function read(cookie) {
return (document.cookie.match('(^|; )' + cookie + '=([^;]*)') || 0)[2]
};
function format(date) {
/*!
* JavaScript Pretty Date
* Copyright (c) 2011 John Resig (ejohn.org)
* Licensed under the MIT and GPL licenses.
*/
var diff = (((new Date()).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31)
return;
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && Math.floor(diff / 60) + " minutes ago" ||
diff < 7200 && "1 hour ago" ||
diff < 86400 && Math.floor(diff / 3600) + " hours ago") ||
day_diff == 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days ago" ||
day_diff < 31 && Math.ceil(day_diff / 7) + " weeks ago";
}
function brew(arr) {
/*
* Element creation utility. Similar to DOMinate, but with a slightly different syntax:
* brew([TAG, {any: attribute, ...}, 'Hello World', ' Foo Bar', ['TAG', 'Hello World'], ...])
* --> <TAG any="attribute">Hello World Foo Bar<TAG>Hello World</TAG></TAG>
*/
var rv = document.createElement(arr[0]);
for (var i = 1; i < arr.length; i++) {
if (arr[i] instanceof Array) {
rv.appendChild(brew(arr[i]));
} else if (typeof(arr[i]) == "string") {
rv.appendChild(document.createTextNode(arr[i]));
} else {
attrs = arr[i] || {};
for (var k in attrs) {
if (!attrs.hasOwnProperty(k)) continue;
rv.setAttribute(k, attrs[k]);
}
}
};
return rv;
}

@ -62,5 +62,5 @@ class Comment(object):
def md5(self):
hv = hashlib.md5()
for key in set(self.fields) - set(['parent', ]):
hv.update(getattr(self, key) or '')
hv.update(getattr(self, key).encode('utf-8', errors="replace") or u'')
return hv.hexdigest()

@ -3,11 +3,9 @@
<title>Hello World</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/static/style.css" />
<!-- script type="text/javascript" src="/js/embed.js"></script -->
<script type="text/javascript" src="/js/ender.js"></script>
<script type="text/javascript" src="/js/isso.js"></script>
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/client.js"></script>
<script data-main="/client/embed" src="/client/require.js"></script>
<style type="text/css">
body {
margin: 0 auto;
@ -46,7 +44,7 @@
<footer>
<hr />
<div id="isso_thread"></div>
<div id="isso-thread"></div>
<noscript>
<p>Please enable JavaScript to view the comments powered by Isso™.</a></p>
</noscript>
@ -54,4 +52,4 @@
</article>
</body>
</body>

@ -1,68 +1,133 @@
#comments {
#isso-comments {
margin: 0;
padding: 0;
}
.isso {
.isso-comment {
margin-bottom: 18px;
margin-top: 18px;
}
.isso footer a, .isso header a {
.isso-follow-up {
margin-left: 64px;
}
.isso-comment footer a, .isso-comment header a {
color: #111111;
font-family: Palatino, "times new roman", serif;
font-weight: bold;
font-size: 0.95em;
text-decoration: none;
}
.isso footer a:hover, .isso header a:hover {
.isso-comment footer a:hover, .isso-comment header a:hover {
color: #111111;
text-shadow: #aaaaaa 0px 0px 1px;
}
.isso .author {
.isso-comment > header, .isso-comment > footer {
font-family: "Helvetica", Arial, sans-serif;
font-size: 0.80em;
}
.isso-comment .author {
font-weight: bold;
color: #555;
}
.isso time {
color: lightgray;
float: right;
.isso-comment .spacer {
padding-left: 6px;
padding-right: 6px;
}
.isso > header {
border-bottom: solid 1px lightgray;
.isso-comment .spacer, .isso-comment .permalink {
color: gray;
font-weight: normal;
}
.isso > header .note {
.isso-comment > header {
margin-bottom: 8px;
margin-left: 64px;
}
.isso-comment > header .note {
float: right;
color: gray;
}
.isso > footer > a {
.isso-comment span.avatar {
position: relative;
margin-top: -22px;
/*margin: 16px 0px 12px 0px;*/
float: left;
clear: both;
}
img:-moz-broken {
-moz-force-broken-image-icon: 1;
}
/* comment style */
.isso-comment > div.text {
margin-left: 64px;
min-height: 24px;
margin-bottom: 4px;
text-align: left;
font-family: Georgia, Times, serif;
}
/* .. */
.isso-comment > footer {
margin-left: 64px;
}
.isso-comment > footer > a {
padding: 0 10px 10px 0;
}
.isso + .issoform {
/* comment form */
.isso-comment + .isso-comment-box {
margin-left: 20px;
}
.issoform input, .issoform textarea {
border: 4px solid rgba(0,0,0,0.1);
padding: 8px 10px;
.isso-comment-box ul {
padding: 0;
}
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
.isso-comment-box .optional {
margin-bottom: 6px;
}
.isso-comment-box li {
margin-right: 8px;
display: inline;
}
.isso-comment-box li input {
margin-left: 0;
}
.isso-comment-box input, .isso-comment-box textarea {
border: 1px solid rgba(0,0,0,0.25);
padding: 8px 10px;
border-radius: 3px;
outline: 0;
}
.issoform textarea {
width: 350px;
.isso-comment-box textarea {
width: 640px;
margin-bottom: 6px;
}
.issoform input[type="submit"] {
.isso-comment-box input[type="submit"] {
cursor: pointer;
background: -webkit-linear-gradient(top, #efefef, #ddd);
background: -moz-linear-gradient(top, #efefef, #ddd);
@ -72,9 +137,10 @@
color: #333;
text-shadow: 0px 1px 1px rgba(255,255,255,1);
border: 1px solid #ccc;
margin-left: 0px;
}
.issoform input[type="submit"]:hover {
.isso-comment-box input[type="submit"]:hover {
background: -webkit-linear-gradient(top, #eee, #ccc);
background: -moz-linear-gradient(top, #eee, #ccc);
background: -ms-linear-gradient(top, #eee, #ccc);
@ -83,7 +149,7 @@
border: 1px solid #bbb;
}
.issoform input[type="submit"]:active {
.isso-comment-box input[type="submit"]:active {
background: -webkit-linear-gradient(top, #ddd, #aaa);
background: -moz-linear-gradient(top, #ddd, #aaa);
background: -ms-linear-gradient(top, #ddd, #aaa);
@ -92,6 +158,25 @@
border: 1px solid #999;
}
.issoform div {
margin-bottom: 8px;
input {
transition: background-color 0.1s ease;
}
input:focus:invalid {
box-shadow: inset 0px 0px 6px #8b0000;
}
textarea:focus:valid, input:focus:valid {
box-shadow: inset 0px 0px 6px #2f4f4f;
}
.isso-popup {
font-family: Helvetica, Arial, sans-serif;
font-size: 0.8em;
padding: 6px 8px;
margin-left: 20px;
border: 1px solid rgb(242, 122, 122);
border-radius: 2px;
background-color: rgb(242, 222, 222);
z-index: 9002;
}

@ -0,0 +1,28 @@
{% extends "base.j2" %}
{% block body %}
<div class="row">
<div class="span4">
<h2>Overview</h1>
<ul>
<li><b>N</b> approved</li>
<li><b>N</b> pending</li>
</ul>
<ul>
<li><b>N</b> Threads</li>
</ul>
</div>
<div class="span8">
<h2>Recent Comments</h1>
</div>
</div>
{% endblock %}

@ -1,105 +0,0 @@
<%inherit file="base.mako"/>
<%
from time import strftime, gmtime
from urllib import quote, urlencode
from urlparse import parse_qsl
def query(**kw):
qs = dict(parse_qsl(request.query_string))
qs.update(kw)
return urlencode(qs)
def get(name, convert):
limit = request.args.get(name)
return convert(limit[0]) if limit is not None else None
%>
<%block name="title">
Isso Dashboard
</%block>
<%def name="make(comment)">
<article class="isso column grid_12" data-path="${quote(comment.path)}" data-id="${comment.id}">
<div class="row">
<div class="column grid_9">
<header>
<span class="title"><a href="${comment.path}">${comment.path}</a></span>
</header>
<div class="text">
${app.markup.convert(comment.text)}
</div>
</div>
<div class="column grid_3 options">
<ul>
<li>${strftime('%d. %B %Y um %H:%M', gmtime(comment.created))}</li>
<li>von <em>${comment.author}</em></li>
% if comment.website:
<li><a href="${comment.website}">${comment.website}</a></li>
% endif
</ul>
<div class="row buttons">
<a href="#" class="red delete column grid_1">Delete</a>
% if comment.pending:
<a href="#" class="green approve column grid_1">Approve</a>
% endif
</div>
</div>
</div>
</article>
</%def>
<div class="row pending red">
<div class="column grid_9">
<h2>Pending</h2>
</div>
<div class="column grid_3">
<span class="limit">
[ <a href="?${query(pendinglimit=10)}">10</a>
| <a href="?${query(pendinglimit=20)}">20</a>
| <a href="?${query(pendinglimit=100000)}">All</a> ]
</span>
</div>
</div>
<div class="row" id="pending">
% for comment in app.db.recent(limit=get('pendinglimit', int), mode=2):
${make(comment)}
% endfor
</div>
<div class="row recent green">
<div class="column grid_9">
<h2>Recent</h2>
</div>
<div class="column grid_3">
<span class="limit">
[ <a href="?${query(recentlimit=10)}">10</a>
| <a href="?${query(recentlimit=20)}">20</a>
| <a href="?${query(recentlimit=100000)}">All</a> ]
</span>
</div>
</div>
<div class="row" id="approved">
% for comment in app.db.recent(limit=get('recentlimit', int) or 20, mode=5):
${make(comment)}
% endfor
</div>
<footer class="row">
<p><a href="https://github.com/posativ/isso">Isso</a> Ich schrei sonst!</p>
</footer>

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Isso: Admin Console</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<!-- Le styles -->
<link href="../static/css/bootstrap.css" rel="stylesheet">
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
#footer {
background-color: #f5f5f5;
position: fixed;
bottom: 0;
width: 100%;
}
.container .credit {
margin: 20px 0;
}
</style>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="../assets/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="brand" href="#">Isso</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li class="active"><a href="#">Dashboard</a></li>
<li><a href="#about">About</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<div class="container">
{% block body %}
{% endblock %}
</div> <!-- /container -->
<footer id="footer">
<div class="container">
<p class="muted credit"><a href="https://github.com/posativ/isso">Isso</a> Ich schrei sonst!</p>
</div>
</footer>
<footer class="footer">
</footer>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="../static/jquery.min.js"></script>
<script src="../static/js/bootstrap.min.js"></script>
</body>
</html>

@ -1,123 +0,0 @@
<!DOCTYPE html>
<head>
<title><%block name="title" /></title>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="/static/style.css" />
<script type="text/javascript" src="/js/interface.js"></script>
<style>
/* ================ */
/* = The 1Kb Grid = */ /* 12 columns, 60 pixels each, with 20 pixel gutter */
/* ================ */
.grid_1 { width: 60px; }
.grid_2 { width: 140px; }
.grid_3 { width: 220px; }
.grid_4 { width: 300px; }
.grid_5 { width: 380px; }
.grid_6 { width: 460px; }
.grid_7 { width: 540px; }
.grid_8 { width: 620px; }
.grid_9 { width: 700px; }
.grid_10 { width: 780px; }
.grid_11 { width: 860px; }
.grid_12 { width: 940px; }
.column {
margin: 0 10px;
overflow: hidden;
float: left;
display: inline;
}
.row {
width: 960px;
margin: 0 auto;
overflow: hidden;
}
.row .row {
margin: 0 -10px;
width: auto;
display: inline-block;
}
* {
margin: 0;
padding: 0;
}
body {
font-size: 1em;
line-height: 1.4;
margin: 20px 0 0 0;
}
body > h1 {
text-align: center;
padding: 8px 0 8px 0;
background-color: yellowgreen;
}
article {
background-color: rgb(245, 245, 245);
box-shadow: 0px 0px 4px 0px;
/*border-radius: 2px;*/
}
article header {
font-size: 1.1em;
}
article .options {
margin: 8px;
padding-bottom: 40px
}
article .text, article header {
padding: 8px 16px 8px 16px;
}
.text p {
margin-bottom: 10px;
text-align: justify;
}
.recent, .pending {
padding: 10px 40px 10px 40px;
box-shadow: 0px 0px 2px 0px;
/*border-radius: 4px;*/
margin: 2px auto 2px auto;
}
.green {
background-color: #B7D798;
}
.red {
background-color: #D79998;
}
body > footer {
border-top: 1px solid #AAA;
padding-top: 8px;
text-align: center;
}
.approve, .delete {
padding-top: 10px;
padding: 5px;
border: 1px solid #666;
color: #000;
text-decoration: none;
}
.buttons {
padding-top: 10px;
}
<%block name="style" />
</style>
</head>
<body>
${self.body()}
</body>

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sign in &middot; Isso</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<!-- Le styles -->
<link href="../static/css/bootstrap.css" rel="stylesheet">
<style type="text/css">
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
max-width: 300px;
padding: 19px 29px 29px;
margin: 0 auto 20px;
background-color: #fff;
border: 1px solid #e5e5e5;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05);
box-shadow: 0 1px 2px rgba(0,0,0,.05);
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin input[type="text"],
.form-signin input[type="password"] {
font-size: 16px;
height: auto;
margin-bottom: 15px;
padding: 7px 9px;
}
</style>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="../assets/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
<form class="form-signin" action="/admin/", method="post">
<h2 class="form-signin-heading">Admin Interface</h2>
<input type="password" class="input-block-level" placeholder="secret" name="password">
<label class="checkbox">
<input type="checkbox" value="remember-me"> Remember me
</label>
<button class="btn btn-large btn-primary" type="submit">Sign in</button>
</form>
</div> <!-- /container -->
</body>
</html>

@ -1,38 +0,0 @@
<%inherit file="base.mako"/>
<%block name="title">
Isso Login
</%block>
<%block name="style">
#login {
margin: 280px 270px 0 270px;
padding: 30px;
background-color: rgb(245, 245, 245);
box-shadow: 0px 0px 1px 0px;
text-align: center;
}
.button {
border: 1px solid #006;
margin-left: 2px;
padding: 2px 4px 2px 4px;
}
#login input {
margin-top: 8px;
text-align: center;
}
#login input:-moz-placeholder, #login input::-webkit-input-placeholder {} {
color: #CCC;
}
</%block>
<!-- login form -->
<div id="login">
<form action="/admin/" method="post">
<input name="secret" placeholder="secret" type="password" />
<input type="submit" name="submit" value="Login" class="button" />
</form>
</div>

@ -9,7 +9,7 @@ import urllib
from itsdangerous import SignatureExpired, BadSignature
from werkzeug.wrappers import Response
from werkzeug.exceptions import abort
from werkzeug.exceptions import abort, BadRequest
from isso import models, utils
@ -24,12 +24,12 @@ class requires:
def dec(app, env, req, *args, **kwargs):
if self.param not in req.args:
abort(400)
raise BadRequest("missing %s query" % self.param)
try:
kwargs[self.param] = self.type(req.args[self.param])
except TypeError:
abort(400)
raise BadRequest("invalid type for %s, expected %s" % (self.param, self.type))
return func(app, env, req, *args, **kwargs)
@ -45,6 +45,7 @@ def create(app, environ, request, uri):
try:
comment = models.Comment.fromjson(request.data)
except ValueError as e:
print(1)
return Response(unicode(e), 400)
for attr in 'author', 'email', 'website':
@ -52,11 +53,13 @@ def create(app, environ, request, uri):
try:
setattr(comment, attr, cgi.escape(getattr(comment, attr)))
except AttributeError:
print(1)
Response('', 400)
try:
rv = app.db.add(uri, comment)
except ValueError:
print(1)
abort(400) # FIXME: custom exception class, error descr
md5 = rv.md5

Loading…
Cancel
Save