import os import re from webob import Request, Response from webob import exc from tempita import HTMLTemplate VIEW_TEMPLATE = HTMLTemplate( """\ {{page.title}}

{{page.title}}

{{if message}}
{{message}}
{{endif}}
{{page.content|html}}

Edit """ ) EDIT_TEMPLATE = HTMLTemplate( """\ Edit: {{page.title}} {{if page.exists}}

Edit: {{page.title}}

{{else}}

Create: {{page.title}}

{{endif}}
Title:
Content: Cancel

Cancel
""" ) 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) 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) path = os.path.normpath(path) if path.endswith("/"): path += "index" if not path.startswith(self.storage_dir): raise exc.HTTPBadRequest("Bad path") 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"(.*?)", 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"]*>(.*?)", content, re.I | re.S) return match.group(1) @property def mtime(self): if not self.exists: return None else: return int(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 = ( """%s%s""" % (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")