/
main.py
280 lines (223 loc) · 8.95 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
from time import time
import datetime
from hashlib import sha1
from markdown2 import markdown
from BeautifulSoup import BeautifulSoup
import conf
# ---------------------------------------- app setup
from flask import Flask, redirect, url_for, session,\
request, render_template, abort, flash, get_flashed_messages, g,\
Response
app = Flask(__name__)
#app.debug = True
# set the secret key. keep this really secret:
app.secret_key = conf.secret_key
app.debug = conf.debug
token_term = 7
# ---------------------------------------- db
from google.appengine.ext import db
class Model(db.Model):
@classmethod
def find_by(self, property_operator, value):
return self.all().filter(property_operator, value).get()
class User(Model):
name = db.StringProperty()
twitter_id = db.IntegerProperty()
oauth_token = db.StringProperty()
oauth_secret = db.StringProperty()
remember_token = db.StringProperty()
remember_token_expires_at = db.DateTimeProperty()
date = db.DateTimeProperty()
def update_remember_token(self):
token = self.name + '-' + sha1('%s%s%s' % (self.name, conf.secret_key, time())).hexdigest()
self.remember_token = token
expires_at = datetime.datetime.now() + datetime.timedelta(days=token_term)
self.remember_token_expires_at = expires_at
def delete_remember_token(self):
self.remember_token = None
self.remember_token_expires_at = None
class Entry(Model):
hashcode = db.StringProperty()
title = db.StringProperty()
body = db.TextProperty()
formated_body_cache = db.TextProperty()
user = db.ReferenceProperty(User)
date = db.DateTimeProperty()
def formated_body(self):
if self.formated_body_cache is None:
self.formated_body_cache = markdown(self.body)
self.save()
return self.formated_body_cache
# ---------------------------------------- auth
from flaskext.oauth import OAuth
oauth = OAuth()
# Use Twitter as example remote application
twitter = oauth.remote_app('twitter',
# unless absolute urls are used to make requests, this will be added
# before all URLs. This is also true for request_token_url and others.
base_url='http://api.twitter.com/1/',
# where flask should look for new request tokens
request_token_url='http://api.twitter.com/oauth/request_token',
# where flask should exchange the token with the remote application
access_token_url='http://api.twitter.com/oauth/access_token',
# twitter knows two authorizatiom URLs. /authorize and /authenticate.
# they mostly work the same, but for sign on /authenticate is
# expected because this will give the user a slightly different
# user interface on the twitter side.
authorize_url='http://api.twitter.com/oauth/authenticate',
# the consumer keys from the twitter application registry.
consumer_key=conf.consumer_key,
consumer_secret=conf.consumer_secret
)
@app.before_request
def before_request():
g.conf = conf
g.user = None
if 'remember_token' in session:
user = User.find_by('remember_token =', session['remember_token'])
if user is not None:
if user.remember_token_expires_at and user.remember_token_expires_at > datetime.datetime.now():
g.user = user
if user.remember_token_expires_at < datetime.datetime.now() + datetime.timedelta(days=1):
# update remember_token
user.update_remember_token()
session['remember_token'] = user.remember_token
db.put(user)
else:
user.delete_remember_token()
db.put(user)
g.twitter_api_key = conf.consumer_key
@twitter.tokengetter
def get_twitter_token():
"""This is used by the API to look for the auth token and secret
it should use for API calls. During the authorization handshake
a temporary set of token and secret is used, but afterwards this
function has to return the token and secret. If you don't want
to store this in the database, consider putting it into the
session instead.
"""
user = g.user
if user is not None:
return user.oauth_token, user.oauth_secret
return None
@app.route('/login')
def login():
return twitter.authorize(callback=url_for('oauth_authorized',
next=request.args.get('next') or request.referrer or None))
@app.route('/logout')
def logout():
if g.user is not None:
g.user.delete_remember_token()
db.put(g.user)
# flash('You were signed out')
return redirect(request.referrer or url_for('index'))
@app.route('/oauth-authorized')
@twitter.authorized_handler
def oauth_authorized(resp):
"""Called after authorization. After this function finished handling,
the OAuth information is removed from the session again. When this
happened, the tokengetter from above is used to retrieve the oauth
token and secret.
Because the remote application could have re-authorized the application
it is necessary to update the values in the database.
If the application redirected back after denying, the response passed
to the function will be `None`. Otherwise a dictionary with the values
the application submitted. Note that Twitter itself does not really
redirect back unless the user clicks on the application name.
"""
next_url = request.args.get('next') or url_for('index')
if resp is None:
# flash(u'You denied the request to sign in.')
return redirect(next_url)
user = User.find_by('twitter_id =', int(resp['user_id']))
if user is None:
user = User(twitter_id = int(resp['user_id']),
name = resp['screen_name'],
oauth_token = resp['oauth_token'],
oauth_secret = resp['oauth_token_secret'],
date = datetime.datetime.now()
)
user.update_remember_token()
db.put(user)
if user.remember_token is None:
user.update_remember_token()
db.put(user)
session['remember_token'] = user.remember_token
# flash('You were signed in')
return redirect(next_url)
# ---------------------------------------- main
@app.route('/', methods=['GET', 'POST'])
def index():
entries = None
if g.user is not None:
entries = Entry.all().filter('user =', g.user).order('-date').fetch(20)
return render_template('index.html', entries=entries)
# --------- entry
@app.route('/e', methods=['POST'])
def post():
if g.user is None:
abort(401)
sha1hash = sha1(('%s%s' % (request.form['title'], request.form['body'])).encode('UTF-8')).hexdigest()
hashcode = ''
for s in sha1hash:
hashcode = hashcode + s
if len(hashcode) < 6:
continue
if Entry.find_by('hashcode =', hashcode) is None:
break
entry = Entry(title = request.form['title'],
body = request.form['body'],
user = g.user,
hashcode = hashcode,
date = datetime.datetime.now()
)
db.put(entry)
return redirect(url_for('entry', hashcode=entry.hashcode))
@app.route('/e/<hashcode>', methods=['GET', 'POST'])
def entry(hashcode):
entry = Entry.find_by('hashcode =', hashcode)
if entry:
if request.method == 'POST':
if g.user is None or entry.user.key() != g.user.key():
abort(401)
else:
if '_delete' in request.form:
entry.delete()
return redirect(url_for('index'))
else:
entry.title = request.form['title']
entry.body = request.form['body']
entry.formated_body_cache = None
db.put(entry)
return redirect(url_for('entry', hashcode=entry.hashcode))
else:
return render_template('entry.html',
entry=entry)
else:
abort(404)
@app.route('/e/<hashcode>/edit')
def edit(hashcode):
entry = Entry.find_by('hashcode =', hashcode)
if entry:
if g.user is None or entry.user.key() != g.user.key():
abort(401)
else:
return render_template('edit.html', entry=entry)
else:
abort(404)
# --------- search
@app.route('/s')
def search():
return render_template('search.html')
# --------- user
@app.route('/<username>')
def user(username):
user = User.find_by('name =', username)
if user is None:
abort(404)
entries = Entry.all().filter('user =', user).order('-date').fetch(20)
return render_template('user.html', user=user, entries=entries)
# ----------------------------------------
if __name__ == '__main__':
from wsgiref.handlers import CGIHandler
CGIHandler().run(app)