From a753045f8b30ff3dcbb56ff43d5c76ef78d0ff57 Mon Sep 17 00:00:00 2001 From: posativ Date: Sun, 16 Dec 2012 19:34:15 +0100 Subject: [PATCH] some documentation and move login to /admin/ --- README | 10 +++++----- isso/__init__.py | 21 +++++++++++---------- isso/admin.py | 21 ++++++++------------- isso/comment.py | 1 - isso/templates/login.mako | 2 +- isso/wsgi.py | 16 +++++++++++++++- 6 files changed, 40 insertions(+), 31 deletions(-) diff --git a/README b/README index 19b06bd..0922556 100644 --- a/README +++ b/README @@ -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]. diff --git a/isso/__init__.py b/isso/__init__.py index 8e531e1..b36f503 100644 --- a/isso/__init__.py +++ b/isso/__init__.py @@ -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() diff --git a/isso/admin.py b/isso/admin.py index 743de2e..47d4b90 100644 --- a/isso/admin.py +++ b/isso/admin.py @@ -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'} diff --git a/isso/comment.py b/isso/comment.py index ab1759b..bbfd306 100644 --- a/isso/comment.py +++ b/isso/comment.py @@ -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: diff --git a/isso/templates/login.mako b/isso/templates/login.mako index ce3b088..08d5866 100644 --- a/isso/templates/login.mako +++ b/isso/templates/login.mako @@ -32,7 +32,7 @@
-
+
diff --git a/isso/wsgi.py b/isso/wsgi.py index 5e214d6..4ad2c98 100644 --- a/isso/wsgi.py +++ b/isso/wsgi.py @@ -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)