Created
July 16, 2009 15:09
-
-
Save bellbind/148463 to your computer and use it in GitHub Desktop.
[GAE] 1File Wiki App
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
One File Wiki Script for Google AppEngine | |
""" | |
import cgi | |
import re | |
import urllib | |
# use_library() from GAE 1.2.3 | |
from google.appengine.dist import use_library | |
use_library("django", "1.0") | |
import google.appengine.ext.db as gaedb | |
import google.appengine.ext.webapp as gaeapp | |
import google.appengine.ext.webapp.template as gaetemplate | |
import google.appengine.api.users as gaeusers | |
# sync with handler url in app.yml as: | |
# - url: /wiki(?:/.*)? | |
urlbase = "/wiki" | |
class Page(gaedb.Expando): | |
name = gaedb.StringProperty() | |
content = gaedb.TextProperty() | |
pass | |
class Top(gaeapp.RequestHandler): | |
urlpattern = r"%s/?" % urlbase | |
def get(self): | |
self.redirect("%s/top" % Wiki.urlprefix) | |
pass | |
pass | |
class Wiki(gaeapp.RequestHandler): | |
urlprefix = "%s/p" % urlbase | |
urlpattern = r"%s(?:/.*)?" % urlprefix | |
def get(self): | |
name = self._name() | |
if not name: | |
self.redirect("%s/top" % self.urlprefix) | |
return | |
page = Wiki.page(name) | |
query = self._parse_query() | |
if "handler" in query: | |
self._plugin_handle_get(query["handler"], query, page) | |
return | |
self._plugin_handle_get("wiki", query, page) | |
html = self._render_page(page) | |
self.response.out.write(html) | |
pass | |
def post(self): | |
name = self._name() | |
if not name: | |
self.redirect("%s/top" % self.urlprefix) | |
return | |
if not Wiki.has_auth(name): | |
self.redirect(gaeusers.create_login_url(self.request.uri)) | |
return | |
query = self._parse_query() | |
if "handler" in query: | |
self._plugin_handle_post(query["handler"], query, page) | |
return | |
page = Wiki.page(name) | |
self._plugin_handle_post("wiki", query, page) | |
page.content = gaedb.Text( | |
self.request.get("content", unicode(page.content))) | |
page.put() | |
self.redirect(Wiki.page_url(name)) | |
pass | |
@staticmethod | |
def page(name): | |
#page = Page.gql("WHERE name = :name", name=name).get() | |
page = Page.all().filter("name =", name).get() | |
if page is None: page = Page(name=name, content=gaedb.Text(u"")) | |
return page | |
_auth_pattern = re.compile( | |
ur"^-\s*\[\[([\w:\.\-\+\/]+)\]\]\s*$", re.M | re.U) | |
@staticmethod | |
def require_auth(name, authpagename): | |
authdata = unicode(Wiki.page(authpagename).content) | |
authnames = Wiki._auth_pattern.findall(authdata) | |
for authname in authnames: | |
if ((authname.endswith(":") and name.startswith(authname)) or | |
name == authname): | |
return True | |
pass | |
return False | |
_line_break_pattern = re.compile(ur"\r\n?", re.M | re.U) | |
@classmethod | |
def plugin(cls, name): | |
plugin_name = "plugin:%s" % name | |
code = unicode(cls.page(plugin_name).content) | |
code = cls._line_break_pattern.subn("\n", code)[0] + "\n" | |
compiled = compile(code, plugin_name, "exec") | |
plugin = {} | |
exec compiled in globals(), plugin | |
return plugin | |
@classmethod | |
def page_url(cls, name): | |
return "%s/%s" % (cls.urlprefix, urllib.quote(name.encode("utf-8"))) | |
@staticmethod | |
def has_auth(name): | |
auth_admin = Wiki.require_auth(name, "system:auth:admin") | |
auth_user = Wiki.require_auth(name, "system:auth:user") | |
if (auth_admin or auth_user) and not gaeusers.get_current_user(): | |
return False | |
if auth_admin and not gaeusers.is_current_user_admin(): | |
return False | |
return True | |
def _render_page(self, page): | |
ctx = { | |
"auth": Wiki.has_auth(page.name), | |
"user": gaeusers.get_current_user(), | |
"login": gaeusers.create_login_url(self.request.uri), | |
"logout": gaeusers.create_logout_url(self.request.uri), | |
"path": Wiki.page_url(page.name), | |
"title": page.name, | |
"header": self._process_content( | |
Wiki.page("template:header"), page), | |
"top": self._process_content(Wiki.page("template:top"), page), | |
"content": self._process_content(page, page), | |
"source": unicode(page.content), | |
"bottom": self._process_content( | |
Wiki.page("template:bottom"), page), | |
} | |
html = template.render(gaetemplate.Context(ctx)) | |
return html | |
def _plugin_handle_get(self, name, query, page): | |
plugin = Wiki.plugin(name) | |
get = plugin.get("get", lambda p, q, req, res: None) | |
try: | |
redirect = get(page, query, self.request, self.response) | |
if redirect: self.redirect(redirect) | |
pass | |
except: | |
import traceback | |
self.response.set_status(500) | |
self.response.headers["Content-Type"] = "text/plain;charset=UTF-8" | |
self.response.out.write(traceback.format_exc()) | |
pass | |
pass | |
def _plugin_handle_post(self, name, query, page): | |
plugin = Wiki.plugin(name) | |
post = plugin.get("post", lambda p, q, req, res: None) | |
try: | |
redirect = post(page, query, self.request, self.response) | |
if redirect: self.redirect(redirect) | |
pass | |
except: | |
import traceback | |
self.response.set_status(500) | |
self.response.headers["Content-Type"] = "text/plain;charset=UTF-8" | |
self.response.out.write(traceback.format_exc()) | |
pass | |
pass | |
def _parse_query(self): | |
return dict(cgi.parse_qsl(self.request.query_string)) | |
def _name(self): | |
if not self.request.path.startswith(self.urlprefix): | |
return None | |
name = self.request.path[(len(self.urlprefix)+1):] | |
return unicode(urllib.unquote(name), "utf-8") | |
def _process_content(self, content_page, target_page): | |
content, count = self._process_pattern.subn( | |
self._wiki_processor(target_page), content_page.content) | |
return content | |
_process_pattern = re.compile( | |
ur"(?:\[\[([\w\s:\.\-\+\/]+)\]\])|(?:{{([\w:]+)(.*?)}})", | |
re.U | re.M | re.S) | |
def _wiki_processor(self, page): | |
def pattern_processor(m): | |
name = m.group(1) | |
if name: | |
display_name = name[(name.rfind(":")+1):] | |
if display_name == "": display_name = name | |
link = u"%s/%s" % (self.urlprefix, name) | |
return u"""<a href='%s'>%s</a>""" % (link, display_name) | |
plugin_name = m.group(2) | |
plugin_arg = m.group(3) | |
return self._render_plugin(page, plugin_name, plugin_arg) | |
return pattern_processor | |
def _render_plugin(self, page, name, arg): | |
plugin = Wiki.plugin(name) | |
inline = plugin.get("inline", lambda p, d: d) | |
try: | |
return inline(page, arg) | |
except: | |
import traceback | |
return traceback.format_exc() | |
pass | |
pass | |
template = gaetemplate.Template(u""" | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> | |
{{header|safe}} | |
<title>{{title}}</title> | |
</head> | |
<body> | |
{{top|safe}} | |
<div id="wiki_page"> | |
<h1 id="wiki_name">{{title}}</h1> | |
<hr id="wiki_separator_content" /> | |
<div id="wiki_content" style="white-space: pre;" | |
>{{content|safe}}</div> | |
<hr id="wiki_separator_edit" /> | |
<div id="wiki_edit" {% if not auth %}style="display:none;"{% endif %}> | |
<form id="wiki_edit_form" action="{{path}}" method="POST"> | |
<div id="wiki_editor_pane"> | |
<textarea id="wiki_editor" name="content" rows="24" style="width:90%;" | |
>{{source}}</textarea> | |
</div> | |
<button id="wiki_edit_submit" type="submit">update</button> | |
</form> | |
</div> | |
<div id="wiki_account"> | |
{% if user %} | |
<span id="wiki_account_user">{{user}}</span> | |
<a id="wiki_logout_link" href="{{logout}}">logout</a> | |
{% else %} | |
<a id="wiki_login_link" href="{{login}}">login</a> | |
{% endif %} | |
</div> | |
</div> | |
{{bottom|safe}} | |
</body> | |
</html> | |
""") | |
application = gaeapp.WSGIApplication( | |
[(Top.urlpattern, Top), (Wiki.urlpattern, Wiki)]) | |
if __name__ == "__main__": | |
from google.appengine.ext.webapp.util import run_wsgi_app | |
run_wsgi_app(application) | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment