Home | History | Annotate | Download | only in comment-example-code
      1 import os
      2 import urllib
      3 import time
      4 import re
      5 from cPickle import load, dump
      6 from webob import Request, Response, html_escape
      7 from webob import exc
      8 
      9 class Commenter(object):
     10 
     11     def __init__(self, app, storage_dir):
     12         self.app = app
     13         self.storage_dir = storage_dir
     14         if not os.path.exists(storage_dir):
     15             os.makedirs(storage_dir)
     16 
     17     def __call__(self, environ, start_response):
     18         req = Request(environ)
     19         if req.path_info_peek() == '.comments':
     20             return self.process_comment(req)(environ, start_response)
     21         # This is the base path of *this* middleware:
     22         base_url = req.application_url
     23         resp = req.get_response(self.app)
     24         if resp.content_type != 'text/html' or resp.status_code != 200:
     25             # Not an HTML response, we don't want to
     26             # do anything to it
     27             return resp(environ, start_response)
     28         # Make sure the content isn't gzipped:
     29         resp.decode_content()
     30         comments = self.get_data(req.url)
     31         body = resp.body
     32         body = self.add_to_end(body, self.format_comments(comments))
     33         body = self.add_to_end(body, self.submit_form(base_url, req))
     34         resp.body = body
     35         return resp(environ, start_response)
     36 
     37     def get_data(self, url):
     38         # Double-quoting makes the filename safe
     39         filename = self.url_filename(url)
     40         if not os.path.exists(filename):
     41             return []
     42         else:
     43             f = open(filename, 'rb')
     44             data = load(f)
     45             f.close()
     46             return data
     47 
     48     def save_data(self, url, data):
     49         filename = self.url_filename(url)
     50         f = open(filename, 'wb')
     51         dump(data, f)
     52         f.close()
     53 
     54     def url_filename(self, url):
     55         return os.path.join(self.storage_dir, urllib.quote(url, ''))
     56 
     57     _end_body_re = re.compile(r'</body.*?>', re.I|re.S)
     58 
     59     def add_to_end(self, html, extra_html):
     60         """
     61         Adds extra_html to the end of the html page (before </body>)
     62         """
     63         match = self._end_body_re.search(html)
     64         if not match:
     65             return html + extra_html
     66         else:
     67             return html[:match.start()] + extra_html + html[match.start():]
     68 
     69     def format_comments(self, comments):
     70         if not comments:
     71             return ''
     72         text = []
     73         text.append('<hr>')
     74         text.append('<h2><a name="comment-area"></a>Comments (%s):</h2>' % len(comments))
     75         for comment in comments:
     76             text.append('<h3><a href="%s">%s</a> at %s:</h3>' % (
     77                 html_escape(comment['homepage']), html_escape(comment['name']),
     78                 time.strftime('%c', comment['time'])))
     79             # Susceptible to XSS attacks!:
     80             text.append(comment['comments'])
     81         return ''.join(text)
     82 
     83     def submit_form(self, base_path, req):
     84         return '''<h2>Leave a comment:</h2>
     85         <form action="%s/.comments" method="POST">
     86          <input type="hidden" name="url" value="%s">
     87          <table width="100%%">
     88           <tr><td>Name:</td>
     89               <td><input type="text" name="name" style="width: 100%%"></td></tr>
     90           <tr><td>URL:</td>
     91               <td><input type="text" name="homepage" style="width: 100%%"></td></tr>
     92          </table>
     93          Comments:<br>
     94          <textarea name="comments" rows=10 style="width: 100%%"></textarea><br>
     95          <input type="submit" value="Submit comment">
     96         </form>
     97         ''' % (base_path, html_escape(req.url))
     98 
     99     def process_comment(self, req):
    100         try:
    101             url = req.params['url']
    102             name = req.params['name']
    103             homepage = req.params['homepage']
    104             comments = req.params['comments']
    105         except KeyError, e:
    106             resp = exc.HTTPBadRequest('Missing parameter: %s' % e)
    107             return resp
    108         data = self.get_data(url)
    109         data.append(dict(
    110             name=name,
    111             homepage=homepage,
    112             comments=comments,
    113             time=time.gmtime()))
    114         self.save_data(url, data)
    115         resp = exc.HTTPSeeOther(location=url+'#comment-area')
    116         return resp
    117 
    118 if __name__ == '__main__':
    119     import optparse
    120     parser = optparse.OptionParser(
    121         usage='%prog --port=PORT BASE_DIRECTORY'
    122         )
    123     parser.add_option(
    124         '-p', '--port',
    125         default='8080',
    126         dest='port',
    127         type='int',
    128         help='Port to serve on (default 8080)')
    129     parser.add_option(
    130         '--comment-data',
    131         default='./comments',
    132         dest='comment_data',
    133         help='Place to put comment data into (default ./comments/)')
    134     options, args = parser.parse_args()
    135     if not args:
    136         parser.error('You must give a BASE_DIRECTORY')
    137     base_dir = args[0]
    138     from paste.urlparser import StaticURLParser
    139     app = StaticURLParser(base_dir)
    140     app = Commenter(app, options.comment_data)
    141     from wsgiref.simple_server import make_server
    142     httpd = make_server('localhost', options.port, app)
    143     print 'Serving on http://localhost:%s' % options.port
    144     try:
    145         httpd.serve_forever()
    146     except KeyboardInterrupt:
    147         print '^C'
    148