some documentation and move login to /admin/

pull/16/head
posativ 12 years ago
parent 588a8c306b
commit a753045f8b

@ -3,9 +3,9 @@ Isso Ich schrei sonst
You love static blog generators (especially [Acrylamid][1] *cough*) and the
only option to interact with the community is [Disqus][2]. There's nothing
wrong with it, but if you care about the privacy of your audience you should
better use a comment system that is under your control. This is, were Isso
comes into play.
wrong with it, but if you care about the privacy of your audience you are
better off with a comment system that is under your control. This is, were
Isso comes into play.
[1]: https://github.com/posativ/acrylamid
[2]: http://disqus.com/
@ -24,7 +24,7 @@ Features/Roadmap
- [x] simple JSON API, hence comments are JavaScript-only
- [x] create comments and modify/delete within a time range as user
- [ ] Ping/Trackback support
- [w] simple admin interface
- [x] simple admin interface
- [w] easy integration, similar to Disqus
- [ ] spam filtering using [http:bl][3]
@ -34,7 +34,7 @@ Development
-----------
*Note:* This project is proudly made with the Not Invented Here syndrome,
instead of `werkzeug` or `bottle` it uses about 100 lines to manage WSGI
instead of `werkzeug` or `bottle` it uses about 150 lines to manage WSGI
and instead of JQuery it uses ender.js.
You'll need [2.6 ≤ python ≤ 2.7][4], [ender.js][5] and [YUI Compressor][6].

@ -28,9 +28,10 @@ sys.setdefaultencoding('utf-8') # we only support UTF-8 and python 2.X :-)
import io
import os
import json
import locale
import traceback
from optparse import OptionParser, make_option, SUPPRESS_HELP
from optparse import OptionParser, make_option
from itsdangerous import URLSafeTimedSerializer
@ -41,6 +42,9 @@ from isso.utils import determine, import_object, IssoEncoder
_dumps = json.dumps
setattr(json, 'dumps', lambda obj, **kw: _dumps(obj, cls=IssoEncoder, **kw))
# set user's preferred locale, XXX conflicts with email.util.parse_date ... m(
# locale.setlocale(locale.LC_ALL, '')
class Isso(object):
@ -74,7 +78,6 @@ class Isso(object):
lambda r: (wsgi.Rule(r[0]), r[1], r[2] if isinstance(r[2], list) else [r[2]]), [
# moderation panel
('/', admin.login, ['HEAD', 'GET', 'POST']),
('/admin/', admin.index, ['HEAD', 'GET', 'POST']),
# assets
@ -86,7 +89,6 @@ class Isso(object):
('/1.0/<(.+?):path>/<(int):id>', comment.get, ['HEAD', 'GET']),
('/1.0/<(.+?):path>/<(int):id>', comment.modify, ['PUT', 'DELETE']),
('/1.0/<(.+?):path>/<(int):id>/approve', comment.approve, 'PUT'),
('/1.0/<(.+?):path>', comment.get, 'GET'),
])
@ -174,8 +176,6 @@ def main():
make_option("--sqlite", dest="sqlite", metavar='FILE', default="/tmp/sqlite.db",
help="use SQLite3 database"),
make_option("--port", dest="port", default=8000, help="webserver port"),
make_option("--debug", dest="production", action="store_false", default=True,
help=SUPPRESS_HELP),
]
parser = OptionParser(option_list=options)
@ -185,7 +185,7 @@ def main():
print 'isso', __version__
sys.exit(0)
app = Isso({'SQLITE': options.sqlite, 'PRODUCTION': options.production, 'MODERATION': True})
app = Isso({'SQLITE': options.sqlite, 'MODERATION': True})
if len(args) > 0 and args[0] == 'import':
if len(args) < 2:
@ -195,7 +195,8 @@ def main():
with io.open(args[1], encoding='utf-8') as fp:
migrate.disqus(app.db, fp.read())
else:
from wsgiref.simple_server import make_server
httpd = make_server('127.0.0.1', 8080, app, server_class=wsgi.ThreadedWSGIServer)
httpd.serve_forever()
sys.exit(0)
from wsgiref.simple_server import make_server
httpd = make_server('127.0.0.1', 8080, app, server_class=wsgi.ThreadedWSGIServer)
httpd.serve_forever()

@ -15,25 +15,20 @@ mako = TemplateLookup(directories=[join(dirname(__file__), 'templates')], input_
render = lambda template, **context: mako.get_template(template).render_unicode(**context)
def login(app, environ, request):
def index(app, environ, request):
if request.method == 'POST':
if request.form.getfirst('secret') == app.SECRET:
return 301, '', {
'Location': '/admin/',
'Set-Cookie': setcookie('admin', app.signer.dumps('*'),
max_age=app.MAX_AGE, path='/')
}
return 200, render('login.mako').encode('utf-8'), {'Content-Type': 'text/html'}
def index(app, environ, request):
try:
app.unsign(request.cookies.get('admin', ''))
except (SignatureExpired, BadSignature):
return 301, '', {'Location': '/'}
max_age=app.MAX_AGE, path='/')}
return 403, '', {}
else:
try:
app.unsign(request.cookies.get('admin', ''))
except (SignatureExpired, BadSignature):
return 200, render('login.mako').encode('utf-8'), {'Content-Type': 'text/html'}
ctx = {'app': app, 'request': request}
return 200, render('admin.mako', **ctx).encode('utf-8'), {'Content-Type': 'text/html'}

@ -14,7 +14,6 @@ from isso import json, models, utils, wsgi
def create(app, environ, request, path):
if app.PRODUCTION and not utils.urlexists(app.HOST, path):
print app.HOST, path
return 400, 'URL does not exist', {}
try:

@ -32,7 +32,7 @@
<!-- login form -->
<div id="login">
<form action="/" method="post">
<form action="/admin/" method="post">
<input name="secret" placeholder="secret" type="password" />
<input type="submit" name="submit" value="Login" class="button" />
</form>

@ -21,6 +21,8 @@ from wsgiref.simple_server import WSGIServer
class Request(object):
"""A ``werkzeug.wrappers.Request``-like object but much less powerful.
Fits exactly the needs of Isso."""
def __init__(self, environ):
@ -66,6 +68,14 @@ class Request(object):
class Rule(str):
"""A quick and dirty approach to URL route creation. It uses the
following format:
- ``<(int):name>`` matches any integer, same for float
- ``<(.+?):path>`` matches any given regular expression
With ``Rule.match(url)`` you can test whether a route a) matches
and if so b) retrieve saved variables."""
repl = {'int': r'[0-9]+', 'float': r'\-?[0-9]+\.[0-9]+'}
@ -94,7 +104,7 @@ class Rule(str):
kwargs = match.groupdict()
for key, value in kwargs.items():
for type in int, float:
for type in int, float: # may convert false-positives
try:
kwargs[key] = type(value)
break
@ -105,6 +115,9 @@ class Rule(str):
def sendfile(filename, root, environ):
"""Return file object if found. This function is heavily inspired by
bottles's `static_file` function and uses the same mechanism to avoid
access to e.g. `/etc/shadow`."""
headers = {}
root = abspath(root) + os.sep
@ -133,6 +146,7 @@ def sendfile(filename, root, environ):
def static(app, environ, request, directory, path):
"""A view that returns the requested path from directory."""
try:
return sendfile(path, join(dirname(__file__), directory), environ)

Loading…
Cancel
Save