| 1 | #!-*- coding:utf-8 -*- |
|---|
| 2 | import os |
|---|
| 3 | import cgi |
|---|
| 4 | import logging |
|---|
| 5 | import wsgiref.handlers |
|---|
| 6 | from google.appengine.ext import db |
|---|
| 7 | from google.appengine.ext import webapp |
|---|
| 8 | from google.appengine.api import users |
|---|
| 9 | from google.appengine.api import mail |
|---|
| 10 | from google.appengine.ext.webapp import template |
|---|
| 11 | |
|---|
| 12 | top_path = '/blog/' |
|---|
| 13 | |
|---|
| 14 | blog_info = { |
|---|
| 15 | 'title' : 'Weblog', |
|---|
| 16 | 'description' : 'my short stories', |
|---|
| 17 | 'admin' : 'admin', |
|---|
| 18 | 'mail' : 'admin@example.com', |
|---|
| 19 | 'link' : '', |
|---|
| 20 | 'root' : top_path, |
|---|
| 21 | 'css' : '/blog/static/blog.css', |
|---|
| 22 | 'rss' : '', |
|---|
| 23 | 'flavour' : 'html', |
|---|
| 24 | 'num_entries' : 5, |
|---|
| 25 | 'plugins' : [], |
|---|
| 26 | } |
|---|
| 27 | |
|---|
| 28 | class Entry(db.Model): |
|---|
| 29 | title = db.StringProperty(required=True) |
|---|
| 30 | link = db.StringProperty(required=True) |
|---|
| 31 | author = db.UserProperty(required=True) |
|---|
| 32 | description = db.StringProperty(required=True, multiline=True) |
|---|
| 33 | timestamp = db.DateTimeProperty(required=True, auto_now=True) |
|---|
| 34 | |
|---|
| 35 | def fixPath(path): |
|---|
| 36 | return path.replace("%25", "%").replace("//", "/") |
|---|
| 37 | |
|---|
| 38 | def authUser(path): |
|---|
| 39 | path = fixPath(path) |
|---|
| 40 | blog_info['author'] = users.get_current_user() |
|---|
| 41 | blog_info['login_url'] = users.create_login_url(path) |
|---|
| 42 | blog_info['logout_url'] = users.create_logout_url(path) |
|---|
| 43 | blog_info['is_admin'] = users.is_current_user_admin() |
|---|
| 44 | |
|---|
| 45 | try: |
|---|
| 46 | plugin_path = os.path.join(os.path.dirname(__file__), "plugins") |
|---|
| 47 | for f in os.listdir(plugin_path): |
|---|
| 48 | name, ext = os.path.splitext(f) |
|---|
| 49 | if ext != '.py' or name == '__init__': continue |
|---|
| 50 | path = top_path[1:-1].replace('/', '.') |
|---|
| 51 | try: |
|---|
| 52 | modname = '%s.plugins.%s' % (path, name) |
|---|
| 53 | logging.info('loading %s' % modname) |
|---|
| 54 | m = __import__(modname, globals(), locals(), ['']) |
|---|
| 55 | blog_info['plugins'].append(m) |
|---|
| 56 | except Exception, e: |
|---|
| 57 | logging.error(e) |
|---|
| 58 | logging.info('loaded plugins') |
|---|
| 59 | except Exception, e: |
|---|
| 60 | logging.error(e) |
|---|
| 61 | |
|---|
| 62 | class MainPage(webapp.RequestHandler): |
|---|
| 63 | def __init__(self): |
|---|
| 64 | self.blog_info = blog_info.copy() |
|---|
| 65 | webapp.RequestHandler.__init__(self) |
|---|
| 66 | |
|---|
| 67 | def new_entry(self, **kwds): |
|---|
| 68 | return Entry( |
|---|
| 69 | title = kwds.pop('title', None), |
|---|
| 70 | link = kwds.pop('link', None), |
|---|
| 71 | author = kwds.pop('link', users.get_current_user()), |
|---|
| 72 | description = kwds.pop('description', None), |
|---|
| 73 | ) |
|---|
| 74 | |
|---|
| 75 | def fill_entry(self, entry): |
|---|
| 76 | entry.category = top_path[:-1] |
|---|
| 77 | entry.linkid = entry.link |
|---|
| 78 | entry.linkkey = self.blog_info['link'] + entry.linkid[1:] |
|---|
| 79 | entry.link = self.blog_info['link'] + "%s.%s" % (entry.linkid[1:], self.blog_info['flavour']) |
|---|
| 80 | |
|---|
| 81 | def init_plugins(self): |
|---|
| 82 | if len(self.blog_info['link']) == 0: |
|---|
| 83 | self.blog_info['link'] = "%s://%s%s" % (self.request.scheme, self.request.host, top_path) |
|---|
| 84 | if len(self.blog_info['rss']) == 0: |
|---|
| 85 | self.blog_info['rss'] = "%s://%s%s%s" % (self.request.scheme, self.request.host, top_path, 'index.rss') |
|---|
| 86 | if self.blog_info['css'][0] == '/': |
|---|
| 87 | self.blog_info['css'] = "%s://%s%s" % (self.request.scheme, self.request.host, self.blog_info['css']) |
|---|
| 88 | for plugin in self.blog_info['plugins']: |
|---|
| 89 | try: |
|---|
| 90 | method = getattr(plugin, 'init') |
|---|
| 91 | if method: |
|---|
| 92 | if method(self): logging.info("initialized plugin: %s" % plugin.__name__) |
|---|
| 93 | except Exception, e: |
|---|
| 94 | self.blog_info['plugins'].remove(plugin) |
|---|
| 95 | |
|---|
| 96 | def call_plugins(self, method, *args): |
|---|
| 97 | ret = 0 |
|---|
| 98 | for plugin in self.blog_info['plugins']: |
|---|
| 99 | try: |
|---|
| 100 | func = getattr(plugin, method) |
|---|
| 101 | if func: |
|---|
| 102 | if func(self, *args): ret = ret + 1 |
|---|
| 103 | except AttributeError, e: |
|---|
| 104 | pass |
|---|
| 105 | if ret: logging.info("%s: called %d functions" % (method, ret)) |
|---|
| 106 | return ret |
|---|
| 107 | |
|---|
| 108 | def get_template(self, method) : |
|---|
| 109 | return os.path.join(os.path.dirname(__file__), |
|---|
| 110 | "templates", "%s.%s" % (method, self.blog_info['flavour'])) |
|---|
| 111 | |
|---|
| 112 | def get_path_ext(self): |
|---|
| 113 | path, ext = os.path.splitext(fixPath(self.request.path)[len(top_path)-1:]) |
|---|
| 114 | if ext: ext = ext[1:] |
|---|
| 115 | return path, ext |
|---|
| 116 | |
|---|
| 117 | def post(self): |
|---|
| 118 | authUser(self.request.path) |
|---|
| 119 | path_info, flavour = self.get_path_ext() |
|---|
| 120 | if not flavour: flavour = self.blog_info['flavour'] |
|---|
| 121 | else: self.blog_info['flavour'] = flavour |
|---|
| 122 | |
|---|
| 123 | self.init_plugins() |
|---|
| 124 | entry = db.GqlQuery('SELECT * FROM Entry WHERE link = :path_info', path_info=path_info).get() |
|---|
| 125 | self.call_plugins('post', entry) |
|---|
| 126 | self.call_plugins('finish', entry) |
|---|
| 127 | self.redirect(top_path[:-1] + "%s.%s" % (path_info, flavour)); |
|---|
| 128 | |
|---|
| 129 | def get(self): |
|---|
| 130 | authUser(self.request.path) |
|---|
| 131 | path_info, flavour = self.get_path_ext() |
|---|
| 132 | if not flavour: flavour = self.blog_info['flavour'] |
|---|
| 133 | else: self.blog_info['flavour'] = flavour |
|---|
| 134 | |
|---|
| 135 | self.init_plugins() |
|---|
| 136 | template_values = { |
|---|
| 137 | 'blog_info' : self.blog_info, |
|---|
| 138 | 'path_info' : path_info, |
|---|
| 139 | } |
|---|
| 140 | name = os.path.basename(path_info) |
|---|
| 141 | if name == 'index': |
|---|
| 142 | path_info = path_info[:-len(name)] |
|---|
| 143 | if path_info and path_info[-1:] != '/': |
|---|
| 144 | self.blog_info['method'] = 'entry' |
|---|
| 145 | entry = db.GqlQuery('SELECT * FROM Entry WHERE link = :path_info', path_info=path_info).get() |
|---|
| 146 | if entry: self.fill_entry(entry) |
|---|
| 147 | self.call_plugins('entry', entry) |
|---|
| 148 | template_values['entry'] = entry |
|---|
| 149 | else: |
|---|
| 150 | self.blog_info['method'] = 'entries' |
|---|
| 151 | entries = [] |
|---|
| 152 | if self.call_plugins('entries', entries) == 0: |
|---|
| 153 | for entry in db.GqlQuery('SELECT * FROM Entry ORDER BY timestamp DESC'): |
|---|
| 154 | if entry.link[:len(path_info)] == path_info: |
|---|
| 155 | self.fill_entry(entry) |
|---|
| 156 | entries.append(entry) |
|---|
| 157 | if len(entries) >= int(self.blog_info['num_entries']): break |
|---|
| 158 | for entry in entries: |
|---|
| 159 | self.call_plugins('entry', entry) |
|---|
| 160 | template_values['entries'] = entries |
|---|
| 161 | path = os.path.join(os.path.dirname(__file__), self.get_template(self.blog_info['method'])) |
|---|
| 162 | content_type = template.render(self.get_template('content_type'), template_values).rstrip('\n') |
|---|
| 163 | self.response.headers['Content-Type'] = content_type |
|---|
| 164 | self.response.out.write(template.render(path, template_values)) |
|---|
| 165 | |
|---|
| 166 | def main(): |
|---|
| 167 | application = webapp.WSGIApplication([(top_path + '.*', MainPage)], debug=True) |
|---|
| 168 | wsgiref.handlers.CGIHandler().run(application) |
|---|
| 169 | |
|---|
| 170 | if __name__ == '__main__': |
|---|
| 171 | main() |
|---|