~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/tornado/demos/appengine/blog.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
#
 
3
# Copyright 2009 Facebook
 
4
#
 
5
# Licensed under the Apache License, Version 2.0 (the "License"); you may
 
6
# not use this file except in compliance with the License. You may obtain
 
7
# a copy of the License at
 
8
#
 
9
#     http://www.apache.org/licenses/LICENSE-2.0
 
10
#
 
11
# Unless required by applicable law or agreed to in writing, software
 
12
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
13
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
14
# License for the specific language governing permissions and limitations
 
15
# under the License.
 
16
 
 
17
import functools
 
18
import markdown
 
19
import os.path
 
20
import re
 
21
import tornado.web
 
22
import tornado.wsgi
 
23
import unicodedata
 
24
import wsgiref.handlers
 
25
 
 
26
from google.appengine.api import users
 
27
from google.appengine.ext import db
 
28
 
 
29
 
 
30
class Entry(db.Model):
 
31
    """A single blog entry."""
 
32
    author = db.UserProperty()
 
33
    title = db.StringProperty(required=True)
 
34
    slug = db.StringProperty(required=True)
 
35
    markdown = db.TextProperty(required=True)
 
36
    html = db.TextProperty(required=True)
 
37
    published = db.DateTimeProperty(auto_now_add=True)
 
38
    updated = db.DateTimeProperty(auto_now=True)
 
39
 
 
40
 
 
41
def administrator(method):
 
42
    """Decorate with this method to restrict to site admins."""
 
43
    @functools.wraps(method)
 
44
    def wrapper(self, *args, **kwargs):
 
45
        if not self.current_user:
 
46
            if self.request.method == "GET":
 
47
                self.redirect(self.get_login_url())
 
48
                return
 
49
            raise tornado.web.HTTPError(403)
 
50
        elif not self.current_user.administrator:
 
51
            if self.request.method == "GET":
 
52
                self.redirect("/")
 
53
                return
 
54
            raise tornado.web.HTTPError(403)
 
55
        else:
 
56
            return method(self, *args, **kwargs)
 
57
    return wrapper
 
58
 
 
59
 
 
60
class BaseHandler(tornado.web.RequestHandler):
 
61
    """Implements Google Accounts authentication methods."""
 
62
    def get_current_user(self):
 
63
        user = users.get_current_user()
 
64
        if user: user.administrator = users.is_current_user_admin()
 
65
        return user
 
66
 
 
67
    def get_login_url(self):
 
68
        return users.create_login_url(self.request.uri)
 
69
 
 
70
    def render_string(self, template_name, **kwargs):
 
71
        # Let the templates access the users module to generate login URLs
 
72
        return tornado.web.RequestHandler.render_string(
 
73
            self, template_name, users=users, **kwargs)
 
74
 
 
75
 
 
76
class HomeHandler(BaseHandler):
 
77
    def get(self):
 
78
        entries = db.Query(Entry).order('-published').fetch(limit=5)
 
79
        if not entries:
 
80
            if not self.current_user or self.current_user.administrator:
 
81
                self.redirect("/compose")
 
82
                return
 
83
        self.render("home.html", entries=entries)
 
84
 
 
85
 
 
86
class EntryHandler(BaseHandler):
 
87
    def get(self, slug):
 
88
        entry = db.Query(Entry).filter("slug =", slug).get()
 
89
        if not entry: raise tornado.web.HTTPError(404)
 
90
        self.render("entry.html", entry=entry)
 
91
 
 
92
 
 
93
class ArchiveHandler(BaseHandler):
 
94
    def get(self):
 
95
        entries = db.Query(Entry).order('-published')
 
96
        self.render("archive.html", entries=entries)
 
97
 
 
98
 
 
99
class FeedHandler(BaseHandler):
 
100
    def get(self):
 
101
        entries = db.Query(Entry).order('-published').fetch(limit=10)
 
102
        self.set_header("Content-Type", "application/atom+xml")
 
103
        self.render("feed.xml", entries=entries)
 
104
 
 
105
 
 
106
class ComposeHandler(BaseHandler):
 
107
    @administrator
 
108
    def get(self):
 
109
        key = self.get_argument("key", None)
 
110
        entry = Entry.get(key) if key else None
 
111
        self.render("compose.html", entry=entry)
 
112
 
 
113
    @administrator
 
114
    def post(self):
 
115
        key = self.get_argument("key", None)
 
116
        if key:
 
117
            entry = Entry.get(key)
 
118
            entry.title = self.get_argument("title")
 
119
            entry.markdown = self.get_argument("markdown")
 
120
            entry.html = markdown.markdown(self.get_argument("markdown"))
 
121
        else:
 
122
            title = self.get_argument("title")
 
123
            slug = unicodedata.normalize("NFKD", title).encode(
 
124
                "ascii", "ignore")
 
125
            slug = re.sub(r"[^\w]+", " ", slug)
 
126
            slug = "-".join(slug.lower().strip().split())
 
127
            if not slug: slug = "entry"
 
128
            while True:
 
129
                existing = db.Query(Entry).filter("slug =", slug).get()
 
130
                if not existing or str(existing.key()) == key:
 
131
                    break
 
132
                slug += "-2"
 
133
            entry = Entry(
 
134
                author=self.current_user,
 
135
                title=title,
 
136
                slug=slug,
 
137
                markdown=self.get_argument("markdown"),
 
138
                html=markdown.markdown(self.get_argument("markdown")),
 
139
            )
 
140
        entry.put()
 
141
        self.redirect("/entry/" + entry.slug)
 
142
 
 
143
 
 
144
class EntryModule(tornado.web.UIModule):
 
145
    def render(self, entry):
 
146
        return self.render_string("modules/entry.html", entry=entry)
 
147
 
 
148
 
 
149
settings = {
 
150
    "blog_title": u"Tornado Blog",
 
151
    "template_path": os.path.join(os.path.dirname(__file__), "templates"),
 
152
    "ui_modules": {"Entry": EntryModule},
 
153
    "xsrf_cookies": True,
 
154
}
 
155
application = tornado.wsgi.WSGIApplication([
 
156
    (r"/", HomeHandler),
 
157
    (r"/archive", ArchiveHandler),
 
158
    (r"/feed", FeedHandler),
 
159
    (r"/entry/([^/]+)", EntryHandler),
 
160
    (r"/compose", ComposeHandler),
 
161
], **settings)
 
162
 
 
163
 
 
164
def main():
 
165
    wsgiref.handlers.CGIHandler().run(application)
 
166
 
 
167
 
 
168
if __name__ == "__main__":
 
169
    main()