| 1 | #!/usr/bin/env python |
|---|
| 2 | |
|---|
| 3 | import operator, os, pickle, sys |
|---|
| 4 | |
|---|
| 5 | import cherrypy |
|---|
| 6 | from formencode import Invalid |
|---|
| 7 | from genshi.input import HTML |
|---|
| 8 | from genshi.filters import HTMLFormFiller, HTMLSanitizer |
|---|
| 9 | |
|---|
| 10 | from geddit.form import LinkForm, CommentForm |
|---|
| 11 | from geddit.lib import ajax, template |
|---|
| 12 | from geddit.model import Link, Comment |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | class Root(object): |
|---|
| 16 | |
|---|
| 17 | def __init__(self, data): |
|---|
| 18 | self.data = data |
|---|
| 19 | |
|---|
| 20 | @cherrypy.expose |
|---|
| 21 | @template.output('index.html') |
|---|
| 22 | def index(self): |
|---|
| 23 | links = sorted(self.data.values(), key=operator.attrgetter('time')) |
|---|
| 24 | return template.render(links=links) |
|---|
| 25 | |
|---|
| 26 | @cherrypy.expose |
|---|
| 27 | @template.output('submit.html') |
|---|
| 28 | def submit(self, cancel=False, **data): |
|---|
| 29 | if cherrypy.request.method == 'POST': |
|---|
| 30 | if cancel: |
|---|
| 31 | raise cherrypy.HTTPRedirect('/') |
|---|
| 32 | form = LinkForm() |
|---|
| 33 | try: |
|---|
| 34 | data = form.to_python(data) |
|---|
| 35 | link = Link(**data) |
|---|
| 36 | self.data[link.id] = link |
|---|
| 37 | raise cherrypy.HTTPRedirect('/') |
|---|
| 38 | except Invalid, e: |
|---|
| 39 | errors = e.unpack_errors() |
|---|
| 40 | else: |
|---|
| 41 | errors = {} |
|---|
| 42 | |
|---|
| 43 | return template.render(errors=errors) | HTMLFormFiller(data=data) |
|---|
| 44 | |
|---|
| 45 | @cherrypy.expose |
|---|
| 46 | @template.output('info.html') |
|---|
| 47 | def info(self, id): |
|---|
| 48 | link = self.data.get(id) |
|---|
| 49 | if not link: |
|---|
| 50 | raise cherrypy.NotFound() |
|---|
| 51 | return template.render(link=link) |
|---|
| 52 | |
|---|
| 53 | @cherrypy.expose |
|---|
| 54 | @template.output('comment.html') |
|---|
| 55 | def comment(self, id, cancel=False, **data): |
|---|
| 56 | link = self.data.get(id) |
|---|
| 57 | if not link: |
|---|
| 58 | raise cherrypy.NotFound() |
|---|
| 59 | if cherrypy.request.method == 'POST': |
|---|
| 60 | if cancel: |
|---|
| 61 | raise cherrypy.HTTPRedirect('/info/%s' % link.id) |
|---|
| 62 | form = CommentForm() |
|---|
| 63 | try: |
|---|
| 64 | data = form.to_python(data) |
|---|
| 65 | markup = HTML(data['content']) | HTMLSanitizer() |
|---|
| 66 | data['content'] = markup.render('xhtml') |
|---|
| 67 | comment = link.add_comment(**data) |
|---|
| 68 | if not ajax.is_xhr(): |
|---|
| 69 | raise cherrypy.HTTPRedirect('/info/%s' % link.id) |
|---|
| 70 | return template.render('_comment.html', comment=comment, |
|---|
| 71 | num=len(link.comments)) |
|---|
| 72 | except Invalid, e: |
|---|
| 73 | errors = e.unpack_errors() |
|---|
| 74 | else: |
|---|
| 75 | errors = {} |
|---|
| 76 | |
|---|
| 77 | if ajax.is_xhr(): |
|---|
| 78 | stream = template.render('_form.html', link=link, errors=errors) |
|---|
| 79 | else: |
|---|
| 80 | stream = template.render(link=link, comment=None, errors=errors) |
|---|
| 81 | return stream | HTMLFormFiller(data=data) |
|---|
| 82 | |
|---|
| 83 | @cherrypy.expose |
|---|
| 84 | @template.output('index.xml', method='xml') |
|---|
| 85 | def feed(self, id=None): |
|---|
| 86 | if id: |
|---|
| 87 | link = self.data.get(id) |
|---|
| 88 | if not link: |
|---|
| 89 | raise cherrypy.NotFound() |
|---|
| 90 | return template.render('info.xml', link=link) |
|---|
| 91 | else: |
|---|
| 92 | links = sorted(self.data.values(), key=operator.attrgetter('time')) |
|---|
| 93 | return template.render(links=links) |
|---|
| 94 | |
|---|
| 95 | |
|---|
| 96 | def main(filename): |
|---|
| 97 | # load data from the pickle file, or initialize it to an empty list |
|---|
| 98 | if os.path.exists(filename): |
|---|
| 99 | fileobj = open(filename, 'rb') |
|---|
| 100 | try: |
|---|
| 101 | data = pickle.load(fileobj) |
|---|
| 102 | finally: |
|---|
| 103 | fileobj.close() |
|---|
| 104 | else: |
|---|
| 105 | data = {} |
|---|
| 106 | |
|---|
| 107 | def _save_data(): |
|---|
| 108 | # save data back to the pickle file |
|---|
| 109 | fileobj = open(filename, 'wb') |
|---|
| 110 | try: |
|---|
| 111 | pickle.dump(data, fileobj) |
|---|
| 112 | finally: |
|---|
| 113 | fileobj.close() |
|---|
| 114 | if hasattr(cherrypy.engine, 'subscribe'): # CherryPy >= 3.1 |
|---|
| 115 | cherrypy.engine.subscribe('stop', _save_data) |
|---|
| 116 | else: |
|---|
| 117 | cherrypy.engine.on_stop_engine_list.append(_save_data) |
|---|
| 118 | |
|---|
| 119 | # Some global configuration; note that this could be moved into a |
|---|
| 120 | # configuration file |
|---|
| 121 | cherrypy.config.update({ |
|---|
| 122 | 'tools.encode.on': True, 'tools.encode.encoding': 'utf-8', |
|---|
| 123 | 'tools.decode.on': True, |
|---|
| 124 | 'tools.trailing_slash.on': True, |
|---|
| 125 | 'tools.staticdir.root': os.path.abspath(os.path.dirname(__file__)), |
|---|
| 126 | }) |
|---|
| 127 | |
|---|
| 128 | cherrypy.quickstart(Root(data), '/', { |
|---|
| 129 | '/media': { |
|---|
| 130 | 'tools.staticdir.on': True, |
|---|
| 131 | 'tools.staticdir.dir': 'static' |
|---|
| 132 | } |
|---|
| 133 | }) |
|---|
| 134 | |
|---|
| 135 | if __name__ == '__main__': |
|---|
| 136 | import formencode |
|---|
| 137 | formencode.api.set_stdtranslation(languages=['en']) |
|---|
| 138 | main(sys.argv[1]) |
|---|