~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/tornado/demos/blog/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 markdown
 
18
import os.path
 
19
import re
 
20
import tornado.auth
 
21
import tornado.database
 
22
import tornado.httpserver
 
23
import tornado.ioloop
 
24
import tornado.options
 
25
import tornado.web
 
26
import unicodedata
 
27
 
 
28
from tornado.options import define, options
 
29
 
 
30
define("port", default=8888, help="run on the given port", type=int)
 
31
define("mysql_host", default="127.0.0.1:3306", help="blog database host")
 
32
define("mysql_database", default="blog", help="blog database name")
 
33
define("mysql_user", default="blog", help="blog database user")
 
34
define("mysql_password", default="blog", help="blog database password")
 
35
 
 
36
 
 
37
class Application(tornado.web.Application):
 
38
    def __init__(self):
 
39
        handlers = [
 
40
            (r"/", HomeHandler),
 
41
            (r"/archive", ArchiveHandler),
 
42
            (r"/feed", FeedHandler),
 
43
            (r"/entry/([^/]+)", EntryHandler),
 
44
            (r"/compose", ComposeHandler),
 
45
            (r"/auth/login", AuthLoginHandler),
 
46
            (r"/auth/logout", AuthLogoutHandler),
 
47
        ]
 
48
        settings = dict(
 
49
            blog_title=u"Tornado Blog",
 
50
            template_path=os.path.join(os.path.dirname(__file__), "templates"),
 
51
            static_path=os.path.join(os.path.dirname(__file__), "static"),
 
52
            ui_modules={"Entry": EntryModule},
 
53
            xsrf_cookies=True,
 
54
            cookie_secret="11oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
 
55
            login_url="/auth/login",
 
56
        )
 
57
        tornado.web.Application.__init__(self, handlers, **settings)
 
58
 
 
59
        # Have one global connection to the blog DB across all handlers
 
60
        self.db = tornado.database.Connection(
 
61
            host=options.mysql_host, database=options.mysql_database,
 
62
            user=options.mysql_user, password=options.mysql_password)
 
63
 
 
64
 
 
65
class BaseHandler(tornado.web.RequestHandler):
 
66
    @property
 
67
    def db(self):
 
68
        return self.application.db
 
69
 
 
70
    def get_current_user(self):
 
71
        user_id = self.get_secure_cookie("user")
 
72
        if not user_id: return None
 
73
        return self.db.get("SELECT * FROM authors WHERE id = %s", int(user_id))
 
74
 
 
75
 
 
76
class HomeHandler(BaseHandler):
 
77
    def get(self):
 
78
        entries = self.db.query("SELECT * FROM entries ORDER BY published "
 
79
                                "DESC LIMIT 5")
 
80
        if not entries:
 
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 = self.db.get("SELECT * FROM entries WHERE slug = %s", slug)
 
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 = self.db.query("SELECT * FROM entries ORDER BY published "
 
96
                                "DESC")
 
97
        self.render("archive.html", entries=entries)
 
98
 
 
99
 
 
100
class FeedHandler(BaseHandler):
 
101
    def get(self):
 
102
        entries = self.db.query("SELECT * FROM entries ORDER BY published "
 
103
                                "DESC LIMIT 10")
 
104
        self.set_header("Content-Type", "application/atom+xml")
 
105
        self.render("feed.xml", entries=entries)
 
106
 
 
107
 
 
108
class ComposeHandler(BaseHandler):
 
109
    @tornado.web.authenticated
 
110
    def get(self):
 
111
        id = self.get_argument("id", None)
 
112
        entry = None
 
113
        if id:
 
114
            entry = self.db.get("SELECT * FROM entries WHERE id = %s", int(id))
 
115
        self.render("compose.html", entry=entry)
 
116
 
 
117
    @tornado.web.authenticated
 
118
    def post(self):
 
119
        id = self.get_argument("id", None)
 
120
        title = self.get_argument("title")
 
121
        text = self.get_argument("markdown")
 
122
        html = markdown.markdown(text)
 
123
        if id:
 
124
            entry = self.db.get("SELECT * FROM entries WHERE id = %s", int(id))
 
125
            if not entry: raise tornado.web.HTTPError(404)
 
126
            slug = entry.slug
 
127
            self.db.execute(
 
128
                "UPDATE entries SET title = %s, markdown = %s, html = %s "
 
129
                "WHERE id = %s", title, text, html, int(id))
 
130
        else:
 
131
            slug = unicodedata.normalize("NFKD", title).encode(
 
132
                "ascii", "ignore")
 
133
            slug = re.sub(r"[^\w]+", " ", slug)
 
134
            slug = "-".join(slug.lower().strip().split())
 
135
            if not slug: slug = "entry"
 
136
            while True:
 
137
                e = self.db.get("SELECT * FROM entries WHERE slug = %s", slug)
 
138
                if not e: break
 
139
                slug += "-2"
 
140
            self.db.execute(
 
141
                "INSERT INTO entries (author_id,title,slug,markdown,html,"
 
142
                "published) VALUES (%s,%s,%s,%s,%s,UTC_TIMESTAMP())",
 
143
                self.current_user.id, title, slug, text, html)
 
144
        self.redirect("/entry/" + slug)
 
145
 
 
146
 
 
147
class AuthLoginHandler(BaseHandler, tornado.auth.GoogleMixin):
 
148
    @tornado.web.asynchronous
 
149
    def get(self):
 
150
        if self.get_argument("openid.mode", None):
 
151
            self.get_authenticated_user(self.async_callback(self._on_auth))
 
152
            return
 
153
        self.authenticate_redirect()
 
154
    
 
155
    def _on_auth(self, user):
 
156
        if not user:
 
157
            raise tornado.web.HTTPError(500, "Google auth failed")
 
158
        author = self.db.get("SELECT * FROM authors WHERE email = %s",
 
159
                             user["email"])
 
160
        if not author:
 
161
            # Auto-create first author
 
162
            any_author = self.db.get("SELECT * FROM authors LIMIT 1")
 
163
            if not any_author:
 
164
                author_id = self.db.execute(
 
165
                    "INSERT INTO authors (email,name) VALUES (%s,%s)",
 
166
                    user["email"], user["name"])
 
167
            else:
 
168
                self.redirect("/")
 
169
                return
 
170
        else:
 
171
            author_id = author["id"]
 
172
        self.set_secure_cookie("user", str(author_id))
 
173
        self.redirect(self.get_argument("next", "/"))
 
174
 
 
175
 
 
176
class AuthLogoutHandler(BaseHandler):
 
177
    def get(self):
 
178
        self.clear_cookie("user")
 
179
        self.redirect(self.get_argument("next", "/"))
 
180
 
 
181
 
 
182
class EntryModule(tornado.web.UIModule):
 
183
    def render(self, entry):
 
184
        return self.render_string("modules/entry.html", entry=entry)
 
185
 
 
186
 
 
187
def main():
 
188
    tornado.options.parse_command_line()
 
189
    http_server = tornado.httpserver.HTTPServer(Application())
 
190
    http_server.listen(options.port)
 
191
    tornado.ioloop.IOLoop.instance().start()
 
192
 
 
193
 
 
194
if __name__ == "__main__":
 
195
    main()