thirdparty/google_appengine/lib/webob/docs/wiki-example-code/example.py
author Mario Ferraro <fadinlight@gmail.com>
Sun, 15 Nov 2009 22:12:20 +0100
changeset 3093 d1be59b6b627
parent 109 620f9b141567
permissions -rwxr-xr-x
GMaps related JS changed to use new google namespace. Google is going to change permanently in the future the way to load its services, so better stay safe. Also this commit shows uses of the new melange.js module. Fixes Issue 634.

import os
import re
from webob import Request, Response
from webob import exc
from tempita import HTMLTemplate

VIEW_TEMPLATE = HTMLTemplate("""\
<html>
 <head>
  <title>{{page.title}}</title>
 </head>
 <body>
<h1>{{page.title}}</h1>
{{if message}}
<div style="background-color: #99f">{{message}}</div>
{{endif}}

<div>{{page.content|html}}</div>

<hr>
<a href="{{req.url}}?action=edit">Edit</a>
 </body>
</html>
""")

EDIT_TEMPLATE = HTMLTemplate("""\
<html>
 <head>
  <title>Edit: {{page.title}}</title>
 </head>
 <body>
{{if page.exists}}
<h1>Edit: {{page.title}}</h1>
{{else}}
<h1>Create: {{page.title}}</h1>
{{endif}}

<form action="{{req.path_url}}" method="POST">
 <input type="hidden" name="mtime" value="{{page.mtime}}">
 Title: <input type="text" name="title" style="width: 70%" value="{{page.title}}"><br>
 Content: <input type="submit" value="Save"> 
 <a href="{{req.path_url}}">Cancel</a>
   <br>
 <textarea name="content" style="width: 100%; height: 75%" rows="40">{{page.content}}</textarea>
   <br>
 <input type="submit" value="Save">
 <a href="{{req.path_url}}">Cancel</a>
</form>
</body></html>
""")

class WikiApp(object):

    view_template = VIEW_TEMPLATE
    edit_template = EDIT_TEMPLATE

    def __init__(self, storage_dir):
        self.storage_dir = os.path.abspath(os.path.normpath(storage_dir))

    def __call__(self, environ, start_response):
        req = Request(environ)
        action = req.params.get('action', 'view')
        page = self.get_page(req.path_info)
        try:
            try:
                meth = getattr(self, 'action_%s_%s' % (action, req.method))
            except AttributeError:
                raise exc.HTTPBadRequest('No such action %r' % action).exception
            resp = meth(req, page)
        except exc.HTTPException, e:
            resp = e
        return resp(environ, start_response)

    def get_page(self, path):
        path = path.lstrip('/')
        if not path:
            path = 'index'
        path = os.path.join(self.storage_dir)
        path = os.path.normpath(path)
        if path.endswith('/'):
            path += 'index'
        if not path.startswith(self.storage_dir):
            raise exc.HTTPBadRequest("Bad path").exception
        path += '.html'
        return Page(path)

    def action_view_GET(self, req, page):
        if not page.exists:
            return exc.HTTPTemporaryRedirect(
                location=req.url + '?action=edit')
        if req.cookies.get('message'):
            message = req.cookies['message']
        else:
            message = None
        text = self.view_template.substitute(
            page=page, req=req, message=message)
        resp = Response(text)
        if message:
            resp.delete_cookie('message')
        else:
            resp.last_modified = page.mtime
            resp.conditional_response = True
        return resp

    def action_view_POST(self, req, page):
        submit_mtime = int(req.params.get('mtime') or '0') or None
        if page.mtime != submit_mtime:
            return exc.HTTPPreconditionFailed(
                "The page has been updated since you started editing it")
        page.set(
            title=req.params['title'],
            content=req.params['content'])
        resp = exc.HTTPSeeOther(
            location=req.path_url)
        resp.set_cookie('message', 'Page updated')
        return resp

    def action_edit_GET(self, req, page):
        text = self.edit_template.substitute(
            page=page, req=req)
        return Response(text)

class Page(object):
    def __init__(self, filename):
        self.filename = filename

    @property
    def exists(self):
        return os.path.exists(self.filename)

    @property
    def title(self):
        if not self.exists:
            # we need to guess the title
            basename = os.path.splitext(os.path.basename(self.filename))[0]
            basename = re.sub(r'[_-]', ' ', basename)
            return basename.capitalize()
        content = self.full_content
        match = re.search(r'<title>(.*?)</title>', content, re.I|re.S)
        return match.group(1)
    
    @property
    def full_content(self):
        f = open(self.filename, 'rb')
        try:
            return f.read()
        finally:
            f.close()
    
    @property
    def content(self):
        if not self.exists:
            return ''
        content = self.full_content
        match = re.search(r'<body[^>]*>(.*?)</body>', content, re.I|re.S)
        return match.group(1)

    @property
    def mtime(self):
        if not self.exists:
            return None
        else:
            return os.stat(self.filename).st_mtime

    def set(self, title, content):
        dir = os.path.dirname(self.filename)
        if not os.path.exists(dir):
            os.makedirs(dir)
        new_content = """<html><head><title>%s</title></head><body>%s</body></html>""" % (
            title, content)
        f = open(self.filename, 'wb')
        f.write(new_content)
        f.close()

if __name__ == '__main__':
    import optparse
    parser = optparse.OptionParser(
        usage='%prog --port=PORT'
        )
    parser.add_option(
        '-p', '--port',
        default='8080',
        dest='port',
        type='int',
        help='Port to serve on (default 8080)')
    parser.add_option(
        '--wiki-data',
        default='./wiki',
        dest='wiki_data',
        help='Place to put wiki data into (default ./wiki/)')
    options, args = parser.parse_args()
    print 'Writing wiki pages to %s' % options.wiki_data
    app = WikiApp(options.wiki_data)
    from wsgiref.simple_server import make_server
    httpd = make_server('localhost', options.port, app)
    print 'Serving on http://localhost:%s' % options.port
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print '^C'