80
|
80
|
|
session_token = request.get_cookie (
|
81
|
81
|
|
key = settings['session']['name'],
|
82
|
82
|
|
secret = settings['cookies']['secret'])
|
83
|
|
- |
|
|
83
|
+ |
|
84
|
84
|
|
if database.is_valid_session (session_token):
|
85
|
85
|
|
return controller ()
|
86
|
86
|
|
else:
|
87
|
87
|
|
redirect (application.get_url ('login'))
|
88
|
|
- |
|
|
88
|
+ |
|
89
|
89
|
|
return wrapper
|
90
|
90
|
|
|
91
|
91
|
|
# Decorator.
|
95
|
95
|
|
session_token = request.get_cookie (
|
96
|
96
|
|
key = settings['session']['name'],
|
97
|
97
|
|
secret = settings['cookies']['secret'])
|
98
|
|
- |
|
|
98
|
+ |
|
99
|
99
|
|
if database.is_valid_session (session_token):
|
100
|
100
|
|
redirect (application.get_url ('user_settings'))
|
101
|
101
|
|
else:
|
102
|
102
|
|
return controller ()
|
103
|
|
- |
|
|
103
|
+ |
|
104
|
104
|
|
return wrapper
|
105
|
105
|
|
|
106
|
106
|
|
# Routes
|
110
|
110
|
|
"""
|
111
|
111
|
|
Display homepage with posts sorted by 'hot'.
|
112
|
112
|
|
"""
|
113
|
|
- |
|
|
113
|
+ |
|
114
|
114
|
|
# Sort order
|
115
|
115
|
|
sort = request.query.sort or 'hot'
|
116
|
|
- |
|
|
116
|
+ |
|
117
|
117
|
|
# Page number
|
118
|
118
|
|
page = int (request.query.page or 0)
|
119
|
|
- |
|
|
119
|
+ |
|
120
|
120
|
|
if page < 0:
|
121
|
121
|
|
redirect (application.get_url ('homepage'))
|
122
|
|
- |
|
|
122
|
+ |
|
123
|
123
|
|
user = session.user ()
|
124
|
|
- |
|
|
124
|
+ |
|
125
|
125
|
|
if sort in [ 'hot', 'new' ]:
|
126
|
126
|
|
posts = database.get_posts (
|
127
|
127
|
|
page, user['id'] if user else None,
|
128
|
128
|
|
sort)
|
129
|
129
|
|
else:
|
130
|
130
|
|
posts = []
|
131
|
|
- |
|
|
131
|
+ |
|
132
|
132
|
|
# Disable browser caching
|
133
|
133
|
|
# Fix issue: https://notabug.org/zPlus/freepost/issues/80
|
134
|
134
|
|
response.set_header('cache-control', 'no-cache, no-store, must-revalidate')
|
135
|
135
|
|
response.set_header('pragma', 'no-cache')
|
136
|
136
|
|
response.set_header('expires', '0')
|
137
|
|
- |
|
|
137
|
+ |
|
138
|
138
|
|
return template ('homepage.html', posts=posts, sort=sort)
|
139
|
139
|
|
|
140
|
140
|
|
# TODO implement this
|
143
|
143
|
|
"""
|
144
|
144
|
|
Display posts by topic.
|
145
|
145
|
|
"""
|
146
|
|
- |
|
|
146
|
+ |
|
147
|
147
|
|
# Sort order
|
148
|
148
|
|
sort = request.query.sort or 'hot'
|
149
|
|
- |
|
|
149
|
+ |
|
150
|
150
|
|
# Page number
|
151
|
151
|
|
page = int (request.query.page or 0)
|
152
|
|
- |
|
|
152
|
+ |
|
153
|
153
|
|
if page < 0:
|
154
|
154
|
|
redirect (application.get_url ('homepage'))
|
155
|
|
- |
|
|
155
|
+ |
|
156
|
156
|
|
user = session.user ()
|
157
|
|
- |
|
|
157
|
+ |
|
158
|
158
|
|
if sort in [ 'hot', 'new' ]:
|
159
|
159
|
|
posts = database.get_posts (
|
160
|
160
|
|
page, user['id'] if user else None,
|
161
|
161
|
|
sort, name)
|
162
|
162
|
|
else:
|
163
|
163
|
|
posts = []
|
164
|
|
- |
|
|
164
|
+ |
|
165
|
165
|
|
return template (
|
166
|
166
|
|
'homepage.html',
|
167
|
167
|
|
topic=name,
|
172
|
172
|
|
"""
|
173
|
173
|
|
Display "About" page.
|
174
|
174
|
|
"""
|
175
|
|
- |
|
|
175
|
+ |
|
176
|
176
|
|
return template ('about.html')
|
177
|
177
|
|
|
178
|
178
|
|
@get ('/login', name='login')
|
190
|
190
|
|
"""
|
191
|
191
|
|
Check login form.
|
192
|
192
|
|
"""
|
193
|
|
- |
|
|
193
|
+ |
|
194
|
194
|
|
username = request.forms.getunicode ('username')
|
195
|
195
|
|
password = request.forms.getunicode ('password')
|
196
|
196
|
|
remember = 'remember' in request.forms
|
197
|
|
- |
|
|
197
|
+ |
|
198
|
198
|
|
if not username or not password:
|
199
|
199
|
|
return template (
|
200
|
200
|
|
'login.html',
|
201
|
201
|
|
flash = 'Bad login!')
|
202
|
|
- |
|
|
202
|
+ |
|
203
|
203
|
|
# Check if user exists
|
204
|
204
|
|
user = database.check_user_credentials (username, password)
|
205
|
|
- |
|
|
205
|
+ |
|
206
|
206
|
|
# Username/Password not working
|
207
|
207
|
|
if not user:
|
208
|
208
|
|
return template (
|
209
|
209
|
|
'login.html',
|
210
|
210
|
|
flash = 'Bad login!')
|
211
|
|
- |
|
|
211
|
+ |
|
212
|
212
|
|
# Delete any existing "reset token"
|
213
|
213
|
|
database.delete_password_reset_token (user['id'])
|
214
|
|
- |
|
|
214
|
+ |
|
215
|
215
|
|
# Start new session
|
216
|
216
|
|
session.start (user['id'], remember)
|
217
|
|
- |
|
|
217
|
+ |
|
218
|
218
|
|
# Redirect logged in user to preferred feed
|
219
|
219
|
|
if user['preferred_feed'] == 'new':
|
220
|
220
|
|
redirect (application.get_url ('homepage') + '?sort=new')
|
227
|
227
|
|
"""
|
228
|
228
|
|
Register new account.
|
229
|
229
|
|
"""
|
230
|
|
- |
|
|
230
|
+ |
|
231
|
231
|
|
return template ('register.html')
|
232
|
232
|
|
|
233
|
233
|
|
@post ('/register')
|
236
|
236
|
|
"""
|
237
|
237
|
|
Check form for creating new account.
|
238
|
238
|
|
"""
|
239
|
|
- |
|
240
|
|
- |
abort()
|
241
|
|
- |
|
|
239
|
+ |
|
242
|
240
|
|
username = request.forms.getunicode ('username')
|
243
|
241
|
|
password = request.forms.getunicode ('password')
|
244
|
|
- |
|
|
242
|
+ |
|
245
|
243
|
|
# Normalize username
|
246
|
244
|
|
username = username.strip ()
|
247
|
|
- |
|
|
245
|
+ |
|
248
|
246
|
|
# Check if username already exists.
|
249
|
247
|
|
# Use case-insensitive match to prevent two similar usernames.
|
250
|
248
|
|
if len (username) == 0 or database.username_exists (username, case_sensitive=False):
|
251
|
249
|
|
return template (
|
252
|
250
|
|
'register.html',
|
253
|
251
|
|
flash='Name taken, please choose another.')
|
254
|
|
- |
|
|
252
|
+ |
|
255
|
253
|
|
# Password too short?
|
256
|
254
|
|
if len (password) < 8:
|
257
|
255
|
|
return template (
|
258
|
256
|
|
'register.html',
|
259
|
257
|
|
flash = 'Password too short')
|
260
|
|
- |
|
|
258
|
+ |
|
261
|
259
|
|
# Username OK, Password OK: create new user
|
262
|
260
|
|
database.new_user (username, password)
|
263
|
|
- |
|
|
261
|
+ |
|
264
|
262
|
|
# Retrieve user (check if it was created)
|
265
|
263
|
|
user = database.check_user_credentials (username, password)
|
266
|
|
- |
|
|
264
|
+ |
|
267
|
265
|
|
# Something bad happened...
|
268
|
266
|
|
if user is None:
|
269
|
267
|
|
return template (
|
270
|
268
|
|
'register.html',
|
271
|
269
|
|
flash = 'An error has occurred, please try again.')
|
272
|
|
- |
|
|
270
|
+ |
|
273
|
271
|
|
# Start session...
|
274
|
272
|
|
session.start (user['id'])
|
275
|
|
- |
|
|
273
|
+ |
|
276
|
274
|
|
# ... and go to the homepage of the new user
|
277
|
275
|
|
redirect (application.get_url ('user_settings'))
|
278
|
276
|
|
|
282
|
280
|
|
"""
|
283
|
281
|
|
Logout user and return to homepage.
|
284
|
282
|
|
"""
|
285
|
|
- |
|
|
283
|
+ |
|
286
|
284
|
|
session.close ()
|
287
|
|
- |
|
|
285
|
+ |
|
288
|
286
|
|
redirect (application.get_url ('homepage'))
|
289
|
287
|
|
|
290
|
288
|
|
@get ('/password_reset', name='password_reset')
|
293
|
291
|
|
"""
|
294
|
292
|
|
Display form to reset users password.
|
295
|
293
|
|
"""
|
296
|
|
- |
|
|
294
|
+ |
|
297
|
295
|
|
return template ('login_reset.html')
|
298
|
296
|
|
|
299
|
297
|
|
@post ('/password_reset', name='password_reset_send_code')
|
303
|
301
|
|
Validate form for resetting password, and if valid send secret
|
304
|
302
|
|
code via email.
|
305
|
303
|
|
"""
|
306
|
|
- |
|
|
304
|
+ |
|
307
|
305
|
|
username = request.forms.getunicode('username')
|
308
|
306
|
|
email = request.forms.getunicode('email')
|
309
|
|
- |
|
|
307
|
+ |
|
310
|
308
|
|
if not username or not email:
|
311
|
309
|
|
redirect(application.get_url('change_password'))
|
312
|
|
- |
|
|
310
|
+ |
|
313
|
311
|
|
user = database.get_user_by_username(username)
|
314
|
|
- |
|
|
312
|
+ |
|
315
|
313
|
|
if not user:
|
316
|
314
|
|
redirect(application.get_url('change_password'))
|
317
|
|
- |
|
|
315
|
+ |
|
318
|
316
|
|
# Make sure the given email matches the one that we have in the database
|
319
|
317
|
|
if user['email'] != email:
|
320
|
318
|
|
redirect(application.get_url('change_password'))
|
321
|
|
- |
|
|
319
|
+ |
|
322
|
320
|
|
# Is there another valid token already (from a previous request)?
|
323
|
321
|
|
# If yes, do not send another one (to prevent multiple requests or spam)
|
324
|
322
|
|
if database.is_password_reset_token_valid(user['id']):
|
325
|
323
|
|
redirect(application.get_url('change_password'))
|
326
|
|
- |
|
|
324
|
+ |
|
327
|
325
|
|
# Generate secret token to send via email
|
328
|
326
|
|
secret_token = random.ascii_string(32)
|
329
|
|
- |
|
|
327
|
+ |
|
330
|
328
|
|
# Add token to database
|
331
|
329
|
|
database.set_password_reset_token(user['id'], secret_token)
|
332
|
|
- |
|
|
330
|
+ |
|
333
|
331
|
|
# Send token via email
|
334
|
332
|
|
client_ip = request.environ.get('HTTP_X_FORWARDED_FOR') or \
|
335
|
333
|
|
request.environ.get('REMOTE_ADDR')
|
339
|
337
|
|
'email/password_reset.txt',
|
340
|
338
|
|
ip=client_ip,
|
341
|
339
|
|
secret_token=secret_token)
|
342
|
|
- |
|
|
340
|
+ |
|
343
|
341
|
|
mail.send(email_to, email_subject, email_body)
|
344
|
|
- |
|
|
342
|
+ |
|
345
|
343
|
|
redirect(application.get_url('change_password'))
|
346
|
344
|
|
|
347
|
345
|
|
@get ('/change_password', name='change_password')
|
351
|
349
|
|
After the secret code was sent via email, display this form where
|
352
|
350
|
|
the user can insert the secret code + new password.
|
353
|
351
|
|
"""
|
354
|
|
- |
|
|
352
|
+ |
|
355
|
353
|
|
return template ('login_change_password.html')
|
356
|
354
|
|
|
357
|
355
|
|
@post ('/change_password', name='validate_new_password')
|
361
|
359
|
|
Validate the new password, check the secret code, and if everything
|
362
|
360
|
|
is OK change the user password.
|
363
|
361
|
|
"""
|
364
|
|
- |
|
|
362
|
+ |
|
365
|
363
|
|
username = request.forms.getunicode('username')
|
366
|
364
|
|
email = request.forms.getunicode('email')
|
367
|
365
|
|
password = request.forms.getunicode('password')
|
368
|
366
|
|
secret_token = request.forms.getunicode('token')
|
369
|
|
- |
|
|
367
|
+ |
|
370
|
368
|
|
# We must have all fields
|
371
|
369
|
|
if not username or not email or not password or not secret_token:
|
372
|
370
|
|
redirect(application.get_url('login'))
|
373
|
|
- |
|
|
371
|
+ |
|
374
|
372
|
|
# Password too short?
|
375
|
373
|
|
if len (password) < 8:
|
376
|
374
|
|
return template (
|
377
|
375
|
|
'login_change_password.html',
|
378
|
376
|
|
flash = 'Password must be at least 8 characters long')
|
379
|
|
- |
|
|
377
|
+ |
|
380
|
378
|
|
# OK, everything should be fine now. Reset user password.
|
381
|
379
|
|
database.reset_password(username, email, password, secret_token)
|
382
|
|
- |
|
|
380
|
+ |
|
383
|
381
|
|
# Check if the password was successfully reset
|
384
|
382
|
|
user = database.check_user_credentials (username, password)
|
385
|
|
- |
|
|
383
|
+ |
|
386
|
384
|
|
# Username/Password not working
|
387
|
385
|
|
if not user:
|
388
|
386
|
|
redirect (application.get_url ('login'))
|
389
|
|
- |
|
|
387
|
+ |
|
390
|
388
|
|
# Everything matched!
|
391
|
389
|
|
# Notify user of password change.
|
392
|
390
|
|
email_to = user['email']
|
393
|
391
|
|
email_subject = 'freepost password changed'
|
394
|
392
|
|
email_body = template ('email/password_changed.txt')
|
395
|
|
- |
|
|
393
|
+ |
|
396
|
394
|
|
mail.send (email_to, email_subject, email_body)
|
397
|
|
- |
|
|
395
|
+ |
|
398
|
396
|
|
# Start new session and redirect user
|
399
|
397
|
|
session.start (user['id'])
|
400
|
398
|
|
redirect (application.get_url ('user_settings'))
|
405
|
403
|
|
"""
|
406
|
404
|
|
A user's personal page.
|
407
|
405
|
|
"""
|
408
|
|
- |
|
|
406
|
+ |
|
409
|
407
|
|
return template ('user_settings.html')
|
410
|
408
|
|
|
411
|
409
|
|
@post ('/user/settings')
|
414
|
412
|
|
"""
|
415
|
413
|
|
Update user info (about, email, ...).
|
416
|
414
|
|
"""
|
417
|
|
- |
|
|
415
|
+ |
|
418
|
416
|
|
user = session.user ()
|
419
|
|
- |
|
|
417
|
+ |
|
420
|
418
|
|
about = request.forms.getunicode ('about')
|
421
|
419
|
|
email = request.forms.getunicode ('email')
|
422
|
420
|
|
preferred_feed = request.forms.getunicode ('preferred_feed')
|
423
|
|
- |
|
|
421
|
+ |
|
424
|
422
|
|
if about is None or email is None:
|
425
|
423
|
|
redirect (application.get_url ('user_settings'))
|
426
|
|
- |
|
|
424
|
+ |
|
427
|
425
|
|
if preferred_feed not in [ 'hot', 'new' ]:
|
428
|
426
|
|
preferred_feed = 'hot'
|
429
|
|
- |
|
|
427
|
+ |
|
430
|
428
|
|
# Update user info in the database
|
431
|
429
|
|
database.update_user (user['id'], about, email, False, preferred_feed)
|
432
|
|
- |
|
|
430
|
+ |
|
433
|
431
|
|
redirect (application.get_url ('user_settings'))
|
434
|
432
|
|
|
435
|
433
|
|
@get ('/user_activity/posts')
|
437
|
435
|
|
def user_posts ():
|
438
|
436
|
|
user = session.user ()
|
439
|
437
|
|
posts = database.get_user_posts (user['id'])
|
440
|
|
- |
|
|
438
|
+ |
|
441
|
439
|
|
return template ('user_posts.html', posts=posts)
|
442
|
440
|
|
|
443
|
441
|
|
@get ('/user_activity/comments')
|
445
|
443
|
|
def user_comments ():
|
446
|
444
|
|
user = session.user ()
|
447
|
445
|
|
comments = database.get_user_comments (user['id'])
|
448
|
|
- |
|
|
446
|
+ |
|
449
|
447
|
|
return template ('user_comments.html', comments=comments)
|
450
|
448
|
|
|
451
|
449
|
|
@get ('/user_activity/replies', name='user_replies')
|
453
|
451
|
|
def user_replies ():
|
454
|
452
|
|
user = session.user ()
|
455
|
453
|
|
replies = database.get_user_replies (user['id'])
|
456
|
|
- |
|
|
454
|
+ |
|
457
|
455
|
|
database.set_replies_as_read (user['id'])
|
458
|
|
- |
|
|
456
|
+ |
|
459
|
457
|
|
return template ('user_replies.html', replies=replies)
|
460
|
458
|
|
|
461
|
459
|
|
@get ('/user/public/<username>', name='user_public')
|
463
|
461
|
|
"""
|
464
|
462
|
|
Display a publicly accessible page with public info about the user.
|
465
|
463
|
|
"""
|
466
|
|
- |
|
|
464
|
+ |
|
467
|
465
|
|
account = database.get_user_by_username (username)
|
468
|
|
- |
|
|
466
|
+ |
|
469
|
467
|
|
if account is None:
|
470
|
468
|
|
redirect (application.get_url ('user_settings'))
|
471
|
|
- |
|
|
469
|
+ |
|
472
|
470
|
|
return template ('user_public_homepage.html', account=account)
|
473
|
471
|
|
|
474
|
472
|
|
@get ('/post/<hash_id>', name='post')
|
476
|
474
|
|
"""
|
477
|
475
|
|
Display a single post with all its comments.
|
478
|
476
|
|
"""
|
479
|
|
- |
|
|
477
|
+ |
|
480
|
478
|
|
user = session.user ()
|
481
|
479
|
|
post = database.get_post (hash_id, user['id'] if user else None)
|
482
|
480
|
|
comments = database.get_post_comments (post['id'], user['id'] if user else None)
|
483
|
481
|
|
topics = database.get_post_topics (post['id'])
|
484
|
|
- |
|
|
482
|
+ |
|
485
|
483
|
|
# Group comments by parent
|
486
|
484
|
|
comments_tree = {}
|
487
|
|
- |
|
|
485
|
+ |
|
488
|
486
|
|
for comment in comments:
|
489
|
487
|
|
if comment['parentId'] is None:
|
490
|
488
|
|
if 0 not in comments_tree:
|
491
|
489
|
|
comments_tree[0] = []
|
492
|
|
- |
|
|
490
|
+ |
|
493
|
491
|
|
comments_tree[0].append(dict(comment))
|
494
|
492
|
|
else:
|
495
|
493
|
|
if comment['parentId'] not in comments_tree:
|
496
|
494
|
|
comments_tree[comment['parentId']] = []
|
497
|
|
- |
|
|
495
|
+ |
|
498
|
496
|
|
comments_tree[comment['parentId']].append(dict(comment))
|
499
|
|
- |
|
|
497
|
+ |
|
500
|
498
|
|
# Build ordered list of comments (recourse tree)
|
501
|
499
|
|
def children (parent_id = 0, depth = 0):
|
502
|
500
|
|
temp_comments = []
|
503
|
|
- |
|
|
501
|
+ |
|
504
|
502
|
|
if parent_id in comments_tree:
|
505
|
503
|
|
for comment in comments_tree[parent_id]:
|
506
|
504
|
|
comment['depth'] = depth
|
507
|
505
|
|
temp_comments.append (comment)
|
508
|
|
- |
|
|
506
|
+ |
|
509
|
507
|
|
temp_comments.extend (children (comment['id'], depth + 1))
|
510
|
|
- |
|
|
508
|
+ |
|
511
|
509
|
|
return temp_comments
|
512
|
|
- |
|
|
510
|
+ |
|
513
|
511
|
|
comments = children ()
|
514
|
|
- |
|
|
512
|
+ |
|
515
|
513
|
|
# Show posts/comments Markdown instead of rendered text
|
516
|
514
|
|
show_source = 'source' in request.query
|
517
|
|
- |
|
|
515
|
+ |
|
518
|
516
|
|
# Disable browser caching
|
519
|
517
|
|
# Fix issue: https://notabug.org/zPlus/freepost/issues/80
|
520
|
518
|
|
response.set_header('cache-control', 'no-cache, no-store, must-revalidate')
|
521
|
519
|
|
response.set_header('pragma', 'no-cache')
|
522
|
520
|
|
response.set_header('expires', '0')
|
523
|
|
- |
|
|
521
|
+ |
|
524
|
522
|
|
return template (
|
525
|
523
|
|
'post.html',
|
526
|
524
|
|
post=post,
|
537
|
535
|
|
def new_comment (hash_id):
|
538
|
536
|
|
# The comment text
|
539
|
537
|
|
comment = request.forms.getunicode ('new_comment').strip ()
|
540
|
|
- |
|
|
538
|
+ |
|
541
|
539
|
|
# Empty comment?
|
542
|
540
|
|
if len (comment) == 0:
|
543
|
541
|
|
redirect (application.get_url ('post', hash_id=hash_id))
|
544
|
|
- |
|
|
542
|
+ |
|
545
|
543
|
|
# Retrieve the post
|
546
|
544
|
|
post = database.get_post (hash_id)
|
547
|
|
- |
|
|
545
|
+ |
|
548
|
546
|
|
# Does post exist?
|
549
|
547
|
|
if not post:
|
550
|
548
|
|
redirect (application.get_url ('homepage'))
|
551
|
|
- |
|
|
549
|
+ |
|
552
|
550
|
|
user = session.user ()
|
553
|
|
- |
|
|
551
|
+ |
|
554
|
552
|
|
comment_hash_id = database.new_comment (comment, hash_id, user['id'], post['userId'])
|
555
|
|
- |
|
|
553
|
+ |
|
556
|
554
|
|
# Retrieve new comment
|
557
|
555
|
|
comment = database.get_comment (comment_hash_id)
|
558
|
|
- |
|
|
556
|
+ |
|
559
|
557
|
|
# Automatically add an upvote for the original poster
|
560
|
558
|
|
database.vote_comment (comment['id'], user['id'], +1)
|
561
|
|
- |
|
|
559
|
+ |
|
562
|
560
|
|
redirect (application.get_url ('post', hash_id=hash_id))
|
563
|
561
|
|
|
564
|
562
|
|
@requires_login
|
566
|
564
|
|
def edit_post (hash_id):
|
567
|
565
|
|
user = session.user ()
|
568
|
566
|
|
post = database.get_post (hash_id, user['id'])
|
569
|
|
- |
|
|
567
|
+ |
|
570
|
568
|
|
# Make sure the session user is the actual poster/commenter
|
571
|
569
|
|
if post['userId'] != user['id']:
|
572
|
570
|
|
redirect (application.get_url ('post', hash_id=hash_id))
|
573
|
|
- |
|
|
571
|
+ |
|
574
|
572
|
|
return template ('edit_post.html', post=post)
|
575
|
573
|
|
|
576
|
574
|
|
@requires_login
|
578
|
576
|
|
def edit_post_check (hash_id):
|
579
|
577
|
|
user = session.user ()
|
580
|
578
|
|
post = database.get_post (hash_id, user['id'])
|
581
|
|
- |
|
|
579
|
+ |
|
582
|
580
|
|
# Make sure the session user is the actual poster/commenter
|
583
|
581
|
|
if post['userId'] != user['id']:
|
584
|
582
|
|
redirect (application.get_url ('homepage'))
|
585
|
|
- |
|
|
583
|
+ |
|
586
|
584
|
|
# MUST have a title. If empty, use original title
|
587
|
585
|
|
title = request.forms.getunicode ('title').strip ()
|
588
|
586
|
|
if len (title) == 0: title = post['title']
|
589
|
587
|
|
link = request.forms.getunicode ('link').strip () if 'link' in request.forms else ''
|
590
|
588
|
|
text = request.forms.getunicode ('text').strip () if 'text' in request.forms else ''
|
591
|
|
- |
|
|
589
|
+ |
|
592
|
590
|
|
# If there is a URL, make sure it has a "scheme"
|
593
|
591
|
|
if len (link) > 0 and urlparse (link).scheme == '':
|
594
|
592
|
|
link = 'http://' + link
|
595
|
|
- |
|
|
593
|
+ |
|
596
|
594
|
|
# Update post
|
597
|
595
|
|
database.update_post (title, link, text, hash_id, user['id'])
|
598
|
|
- |
|
|
596
|
+ |
|
599
|
597
|
|
redirect (application.get_url ('post', hash_id=hash_id))
|
600
|
598
|
|
|
601
|
599
|
|
@requires_login
|
603
|
601
|
|
def edit_comment (hash_id):
|
604
|
602
|
|
user = session.user ()
|
605
|
603
|
|
comment = database.get_comment (hash_id, user['id'])
|
606
|
|
- |
|
|
604
|
+ |
|
607
|
605
|
|
# Make sure the session user is the actual poster/commenter
|
608
|
606
|
|
if comment['userId'] != user['id']:
|
609
|
607
|
|
redirect (application.get_url ('post', hash_id=comment['postHashId']))
|
610
|
|
- |
|
|
608
|
+ |
|
611
|
609
|
|
return template ('edit_comment.html', comment=comment)
|
612
|
610
|
|
|
613
|
611
|
|
@requires_login
|
615
|
613
|
|
def edit_comment_check (hash_id):
|
616
|
614
|
|
user = session.user ()
|
617
|
615
|
|
comment = database.get_comment (hash_id, user['id'])
|
618
|
|
- |
|
|
616
|
+ |
|
619
|
617
|
|
# Make sure the session user is the actual poster/commenter
|
620
|
618
|
|
if comment['userId'] != user['id']:
|
621
|
619
|
|
redirect (application.get_url ('homepage'))
|
622
|
|
- |
|
|
620
|
+ |
|
623
|
621
|
|
text = request.forms.getunicode ('text').strip () if 'text' in request.forms else ''
|
624
|
|
- |
|
|
622
|
+ |
|
625
|
623
|
|
# Only update if the text is not empty
|
626
|
624
|
|
if len (text) > 0:
|
627
|
625
|
|
database.update_comment (text, hash_id, user['id'])
|
628
|
|
- |
|
|
626
|
+ |
|
629
|
627
|
|
redirect (application.get_url ('post', hash_id=comment['postHashId']) + '#comment-' + hash_id)
|
630
|
628
|
|
|
631
|
629
|
|
@get ('/submit')
|
634
|
632
|
|
"""
|
635
|
633
|
|
Submit a new post.
|
636
|
634
|
|
"""
|
637
|
|
- |
|
|
635
|
+ |
|
638
|
636
|
|
return template ('submit.html')
|
639
|
637
|
|
|
640
|
638
|
|
@post ('/submit', name='submit')
|
643
|
641
|
|
"""
|
644
|
642
|
|
Check submission of new post.
|
645
|
643
|
|
"""
|
646
|
|
- |
|
|
644
|
+ |
|
647
|
645
|
|
# Somebody sent a <form> without a title???
|
648
|
646
|
|
if not request.forms.getunicode ('title'):
|
649
|
647
|
|
abort ()
|
650
|
|
- |
|
|
648
|
+ |
|
651
|
649
|
|
user = session.user ()
|
652
|
|
- |
|
|
650
|
+ |
|
653
|
651
|
|
# Retrieve title
|
654
|
652
|
|
title = request.forms.getunicode ('title').strip ()
|
655
|
|
- |
|
|
653
|
+ |
|
656
|
654
|
|
# Bad title?
|
657
|
655
|
|
if len (title) == 0:
|
658
|
656
|
|
return template ('submit.html', flash='Title is missing.')
|
659
|
|
- |
|
|
657
|
+ |
|
660
|
658
|
|
# Retrieve link
|
661
|
659
|
|
link = request.forms.getunicode ('link').strip ()
|
662
|
|
- |
|
|
660
|
+ |
|
663
|
661
|
|
if len (link) > 0:
|
664
|
662
|
|
# If there is a URL, make sure it has a "scheme"
|
665
|
663
|
|
if urlparse (link).scheme == '':
|
666
|
664
|
|
link = 'http://' + link
|
667
|
|
- |
|
|
665
|
+ |
|
668
|
666
|
|
# Check if this link was already posted in order to avoid duplicate posting.
|
669
|
667
|
|
previous_posts = database.link_exists(link)
|
670
|
668
|
|
if previous_posts:
|
673
|
671
|
|
link = application.get_url ('post', hash_id=post['hashId']),
|
674
|
672
|
|
title = post['title'])
|
675
|
673
|
|
for post in previous_posts ])
|
676
|
|
- |
|
|
674
|
+ |
|
677
|
675
|
|
return template ('submit.html',
|
678
|
676
|
|
flash='This link was already submitted:<ul>{posts}</ul>'.format(posts=posts_list))
|
679
|
|
- |
|
|
677
|
+ |
|
680
|
678
|
|
# Retrieve topics
|
681
|
679
|
|
topics = request.forms.getunicode ('topics')
|
682
|
|
- |
|
|
680
|
+ |
|
683
|
681
|
|
# Retrieve text
|
684
|
682
|
|
text = request.forms.getunicode ('text')
|
685
|
|
- |
|
|
683
|
+ |
|
686
|
684
|
|
# Add the new post
|
687
|
685
|
|
post_hash_id = database.new_post (title, link, text, user['id'])
|
688
|
|
- |
|
|
686
|
+ |
|
689
|
687
|
|
# Retrieve the new post just created
|
690
|
688
|
|
post = database.get_post (post_hash_id)
|
691
|
|
- |
|
|
689
|
+ |
|
692
|
690
|
|
# Add topics for this post
|
693
|
691
|
|
database.replace_post_topics (post['id'], topics)
|
694
|
|
- |
|
|
692
|
+ |
|
695
|
693
|
|
# Automatically add an upvote for the original poster
|
696
|
694
|
|
database.vote_post (post['id'], user['id'], +1)
|
697
|
|
- |
|
|
695
|
+ |
|
698
|
696
|
|
# Posted. Now go the new post's page
|
699
|
697
|
|
redirect (application.get_url ('post', hash_id=post_hash_id))
|
700
|
698
|
|
|
702
|
700
|
|
@get ('/reply/<hash_id>', name='reply')
|
703
|
701
|
|
def reply (hash_id):
|
704
|
702
|
|
user = session.user ()
|
705
|
|
- |
|
|
703
|
+ |
|
706
|
704
|
|
# The comment to reply to
|
707
|
705
|
|
comment = database.get_comment (hash_id)
|
708
|
|
- |
|
|
706
|
+ |
|
709
|
707
|
|
# Does the comment exist?
|
710
|
708
|
|
if not comment:
|
711
|
709
|
|
redirect (application.get_url ('homepage'))
|
712
|
|
- |
|
|
710
|
+ |
|
713
|
711
|
|
return template ('reply.html', comment=comment)
|
714
|
712
|
|
|
715
|
713
|
|
@requires_login
|
716
|
714
|
|
@post ('/reply/<hash_id>')
|
717
|
715
|
|
def reply_check (hash_id):
|
718
|
716
|
|
user = session.user ()
|
719
|
|
- |
|
|
717
|
+ |
|
720
|
718
|
|
# The comment to reply to
|
721
|
719
|
|
comment = database.get_comment (hash_id)
|
722
|
|
- |
|
|
720
|
+ |
|
723
|
721
|
|
# The text of the reply
|
724
|
722
|
|
text = request.forms.getunicode ('text').strip ()
|
725
|
|
- |
|
|
723
|
+ |
|
726
|
724
|
|
# Empty comment. Redirect to parent post
|
727
|
725
|
|
if len (text) == 0:
|
728
|
726
|
|
redirect (application.get_url ('post', hash_id=comment['postHashId']))
|
729
|
|
- |
|
|
727
|
+ |
|
730
|
728
|
|
# We have a text, add the reply and redirect to the new reply
|
731
|
729
|
|
reply_hash_id = database.new_comment (
|
732
|
730
|
|
text, comment['postHashId'], user['id'],
|
733
|
731
|
|
comment['userId'], comment['id'])
|
734
|
|
- |
|
|
732
|
+ |
|
735
|
733
|
|
# Retrieve new comment
|
736
|
734
|
|
comment = database.get_comment (reply_hash_id)
|
737
|
|
- |
|
|
735
|
+ |
|
738
|
736
|
|
# Automatically add an upvote for the original poster
|
739
|
737
|
|
database.vote_comment (comment['id'], user['id'], +1)
|
740
|
|
- |
|
|
738
|
+ |
|
741
|
739
|
|
redirect (application.get_url ('post', hash_id=comment['postHashId']) + '#comment-' + reply_hash_id)
|
742
|
740
|
|
|
743
|
741
|
|
@requires_login
|
746
|
744
|
|
"""
|
747
|
745
|
|
Handle upvotes and downvotes.
|
748
|
746
|
|
"""
|
749
|
|
- |
|
|
747
|
+ |
|
750
|
748
|
|
user = session.user ()
|
751
|
|
- |
|
|
749
|
+ |
|
752
|
750
|
|
# Vote a post
|
753
|
751
|
|
if request.forms.getunicode ('target') == 'post':
|
754
|
752
|
|
# Retrieve the post
|
755
|
753
|
|
post = database.get_post (request.forms.getunicode ('post'), user['id'])
|
756
|
|
- |
|
|
754
|
+ |
|
757
|
755
|
|
if not post:
|
758
|
756
|
|
return
|
759
|
|
- |
|
|
757
|
+ |
|
760
|
758
|
|
# If user clicked the "upvote" button...
|
761
|
759
|
|
if request.forms.getunicode ('updown') == 'up':
|
762
|
760
|
|
# If user has upvoted this post before...
|
771
|
769
|
|
else:
|
772
|
770
|
|
# Add +1
|
773
|
771
|
|
database.vote_post (post['id'], user['id'], +1)
|
774
|
|
- |
|
|
772
|
+ |
|
775
|
773
|
|
# If user clicked the "downvote" button...
|
776
|
774
|
|
if request.forms.getunicode ('updown') == 'down':
|
777
|
775
|
|
# If user has downvoted this post before...
|
786
|
784
|
|
else:
|
787
|
785
|
|
# Add -1
|
788
|
786
|
|
database.vote_post (post['id'], user['id'], -1)
|
789
|
|
- |
|
|
787
|
+ |
|
790
|
788
|
|
# Vote a comment
|
791
|
789
|
|
if request.forms.getunicode ('target') == 'comment':
|
792
|
790
|
|
# Retrieve the comment
|
793
|
791
|
|
comment = database.get_comment (request.forms.getunicode ('comment'), user['id'])
|
794
|
|
- |
|
|
792
|
+ |
|
795
|
793
|
|
if not comment:
|
796
|
794
|
|
return
|
797
|
|
- |
|
|
795
|
+ |
|
798
|
796
|
|
# If user clicked the "upvote" button...
|
799
|
797
|
|
if request.forms.getunicode ('updown') == 'up':
|
800
|
798
|
|
# If user has upvoted this comment before...
|
809
|
807
|
|
else:
|
810
|
808
|
|
# Add +1
|
811
|
809
|
|
database.vote_comment (comment['id'], user['id'], +1)
|
812
|
|
- |
|
|
810
|
+ |
|
813
|
811
|
|
# If user clicked the "downvote" button...
|
814
|
812
|
|
if request.forms.getunicode ('updown') == 'down':
|
815
|
813
|
|
# If user has downvoted this comment before...
|
830
|
828
|
|
"""
|
831
|
829
|
|
Search content on this instance, and display the results.
|
832
|
830
|
|
"""
|
833
|
|
- |
|
|
831
|
+ |
|
834
|
832
|
|
# Get the search query
|
835
|
833
|
|
query = request.query.getunicode ('q')
|
836
|
|
- |
|
|
834
|
+ |
|
837
|
835
|
|
# Get the offset
|
838
|
836
|
|
page = int (request.query.getunicode ('page') or 0)
|
839
|
|
- |
|
|
837
|
+ |
|
840
|
838
|
|
if page < 0:
|
841
|
839
|
|
return "Page cannot be less than zero."
|
842
|
|
- |
|
|
840
|
+ |
|
843
|
841
|
|
# Page increment
|
844
|
842
|
|
if 'next' in request.query:
|
845
|
843
|
|
page += 1
|
846
|
844
|
|
if 'previous' in request.query:
|
847
|
845
|
|
page -= 1
|
848
|
|
- |
|
|
846
|
+ |
|
849
|
847
|
|
# Results order
|
850
|
848
|
|
sort = request.query.getunicode ('sort')
|
851
|
849
|
|
if sort not in [ 'newest', 'points' ]:
|
852
|
850
|
|
sort = 'newest'
|
853
|
|
- |
|
|
851
|
+ |
|
854
|
852
|
|
posts = database.search (query, sort=sort, page=page) or []
|
855
|
|
- |
|
|
853
|
+ |
|
856
|
854
|
|
return template ('search.html', posts=posts)
|
857
|
855
|
|
|
858
|
856
|
|
@get ('/rss')
|
860
|
858
|
|
"""
|
861
|
859
|
|
Redirect base RSS URL to default "hot" feed.
|
862
|
860
|
|
"""
|
863
|
|
- |
|
|
861
|
+ |
|
864
|
862
|
|
return redirect (application.get_url ('rss', sorting='hot'))
|
865
|
863
|
|
|
866
|
864
|
|
@get ('/rss/<sorting>', name='rss')
|
868
|
866
|
|
"""
|
869
|
867
|
|
Output an RSS feed of posts.
|
870
|
868
|
|
"""
|
871
|
|
- |
|
|
869
|
+ |
|
872
|
870
|
|
posts = []
|
873
|
|
- |
|
|
871
|
+ |
|
874
|
872
|
|
# Retrieve the hostname from the HTTP request.
|
875
|
873
|
|
# This is used to build absolute URLs in the RSS feed.
|
876
|
874
|
|
base_url = request.urlparts.scheme + '://' + request.urlparts.netloc
|
877
|
|
- |
|
|
875
|
+ |
|
878
|
876
|
|
if sorting == 'new':
|
879
|
877
|
|
posts = database.get_posts (sort='new')
|
880
|
|
- |
|
|
878
|
+ |
|
881
|
879
|
|
if sorting == 'hot':
|
882
|
880
|
|
posts = database.get_posts (sort='hot')
|
883
|
|
- |
|
|
881
|
+ |
|
884
|
882
|
|
# Set correct Content-Type header for this RSS feed
|
885
|
883
|
|
response.content_type = 'application/rss+xml; charset=UTF-8'
|
886
|
|
- |
|
|
884
|
+ |
|
887
|
885
|
|
return template ('rss.xml', base_url=base_url, posts=posts)
|
888
|
886
|
|
|
889
|
887
|
|
@get ('/rss_comments', name='rss_comments')
|
891
|
889
|
|
"""
|
892
|
890
|
|
Output an RSS feed of the latest comments.
|
893
|
891
|
|
"""
|
894
|
|
- |
|
|
892
|
+ |
|
895
|
893
|
|
# Retrieve the hostname from the HTTP request.
|
896
|
894
|
|
# This is used to build absolute URLs in the RSS feed.
|
897
|
895
|
|
base_url = request.urlparts.scheme + '://' + request.urlparts.netloc
|
898
|
|
- |
|
|
896
|
+ |
|
899
|
897
|
|
comments = database.get_latest_comments ()
|
900
|
|
- |
|
|
898
|
+ |
|
901
|
899
|
|
# Set correct Content-Type header for this RSS feed
|
902
|
900
|
|
response.content_type = 'application/rss+xml; charset=UTF-8'
|
903
|
|
- |
|
|
901
|
+ |
|
904
|
902
|
|
return template ('rss_comments.xml', base_url=base_url, comments=comments)
|
905
|
903
|
|
|
906
|
904
|
|
@get ('/<filename:path>', name='static')
|
908
|
906
|
|
"""
|
909
|
907
|
|
Serve static files.
|
910
|
908
|
|
"""
|
911
|
|
- |
|
|
909
|
+ |
|
912
|
910
|
|
return static_file (filename, root='freepost/static/')
|
913
|
911
|
|
|
914
|
912
|
|
|