diff --git a/isso/__init__.py b/isso/__init__.py index 4fe1faa..841f178 100644 --- a/isso/__init__.py +++ b/isso/__init__.py @@ -20,7 +20,7 @@ # # Isso – a lightweight Disqus alternative -__version__ = '0.1' +__version__ = '0.2' import json @@ -33,22 +33,24 @@ from werkzeug.exceptions import HTTPException, NotFound, InternalServerError from isso import admin, comment, db, utils - +# override default json :func:`dumps`. _dumps = json.dumps setattr(json, 'dumps', lambda obj, **kw: _dumps(obj, cls=utils.IssoEncoder, **kw)) - +# yep. lazy. url = lambda path, endpoint, methods: Rule(path, endpoint=endpoint, methods=methods) + url_map = Map([ # moderation panel url('/', 'admin.index', ['GET', 'POST']), - # comments API - url('/comment//', 'comment.get', ['GET']), - url('/comment//new', 'comment.create', ['POST']), - url('/comment//', 'comment.get', ['GET']), - url('/comment//', 'comment.modify', ['PUT', 'DELETE']), -]) + # comment API, note that the client side quotes the URL, but this is + # actually unnecessary. PEP 333 aka WSGI always unquotes PATH_INFO. + url('/comment//', 'comment.get', ['GET']), + url('/comment//new', 'comment.create', ['POST']), + url('/comment//', 'comment.get', ['GET']), + url('/comment//', 'comment.modify', ['PUT', 'DELETE']), +], converters={'re': utils.RegexConverter}) class Isso: @@ -59,7 +61,7 @@ class Isso: SQLITE = None HOST = 'http://localhost:8000/' - MAX_AGE = 15*60 + MAX_AGE = 15 * 60 def __init__(self, conf): @@ -101,5 +103,10 @@ class Isso: def main(): - app = Isso({'SQLITE': '/tmp/sqlite.db'}) - run_simple('127.0.0.1', 8080, app) + from os.path import join, dirname + from werkzeug.wsgi import SharedDataMiddleware + + app = Isso({'SQLITE': '/tmp/sqlite.db', 'PRODUCTION': False}) + app = SharedDataMiddleware(app,{ + '/static': join(dirname(__file__), 'static')}) + run_simple('127.0.0.1', 8000, app, use_reloader=True) diff --git a/isso/comment.py b/isso/comment.py index b4ec839..820fa1b 100644 --- a/isso/comment.py +++ b/isso/comment.py @@ -13,7 +13,7 @@ from isso import json, models, utils def create(app, environ, request, path): - if app.PRODUCTION and not utils.urlexists(app.HOST, path): + if app.PRODUCTION and not utils.urlexists(app.HOST, '/' + path): return abort(404) try: diff --git a/isso/db.py b/isso/db.py index bdb5152..9e83e87 100644 --- a/isso/db.py +++ b/isso/db.py @@ -99,7 +99,7 @@ class SQLite(Abstract): def add(self, path, c): with sqlite3.connect(self.dbpath) as con: keys = ','.join(self.fields) - values = ','.join('?'*len(self.fields)) + values = ','.join('?' * len(self.fields)) con.execute('INSERT INTO comments (%s) VALUES (%s);' % (keys, values), ( 0, path, c.created, c.modified, c.text, c.author, c.email, c.website, c.parent, self.mode) diff --git a/isso/utils.py b/isso/utils.py index 726d087..34e523e 100644 --- a/isso/utils.py +++ b/isso/utils.py @@ -8,6 +8,7 @@ import socket import httplib import urlparse import contextlib +import werkzeug.routing from isso.models import Comment @@ -21,6 +22,12 @@ class IssoEncoder(json.JSONEncoder): return json.JSONEncoder.default(self, obj) +class RegexConverter(werkzeug.routing.BaseConverter): + def __init__(self, url_map, *items): + super(RegexConverter, self).__init__(url_map) + self.regex = items[0] + + def urlexists(host, path): with contextlib.closing(httplib.HTTPConnection(host)) as con: try: diff --git a/specs/test_comment.py b/specs/test_comment.py index ef04e96..6159db4 100644 --- a/specs/test_comment.py +++ b/specs/test_comment.py @@ -1,4 +1,5 @@ +import urllib import tempfile import unittest @@ -64,7 +65,7 @@ class TestComments(unittest.TestCase): def testGetInvalid(self): assert self.get('/comment/path/123').status_code == 404 - assert self.get('/comment/path/spam').status_code == 404 + assert self.get('/comment/path/spam/123').status_code == 404 assert self.get('/comment/foo/').status_code == 404 def testUpdate(self): @@ -102,3 +103,15 @@ class TestComments(unittest.TestCase): assert self.get('/comment/path/1').status_code == 200 assert self.get('/comment/path/2').status_code == 200 + + def testPathVariations(self): + + paths = ['/sub/path/', '/path.html', '/sub/path.html', '%2Fpath/%2F'] + + for path in paths: + assert self.post('/comment/' + path + '/new', + data=json.dumps(comment(text='...'))).status_code == 201 + + for path in paths: + assert self.get('/comment/' + path) + assert self.get('/comment/' + path + '/1')