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}}
"""
)
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")