-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.py
More file actions
965 lines (859 loc) · 36.4 KB
/
Copy pathmain.py
File metadata and controls
965 lines (859 loc) · 36.4 KB
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
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
import os
import re
import random
import webapp2
import hashlib
import hmac
import datetime
from string import letters
# import jinja2 lib for templating
import jinja2
# import google app engine data store lib
from google.appengine.ext import db
from google.appengine.ext.db import metadata
__author__ = "Harry Staley <staleyh@gmail.com>"
__version__ = "1.0"
# FILE LEVEL VARIABLES/CONSTANTS
# sets the locaiton of the templates folder contained in the home of this file.
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), 'templates')
# envokes the jinja2 envronment pointing it to the location of the templates.
# with user input markup automatically escaped
JINJA_ENV = jinja2.Environment(loader=jinja2.FileSystemLoader(TEMPLATE_DIR),
autoescape=True)
COOKIE_SECRET = 'secret'
# REGEX FOR SIGNUP FORM
# email validation regex from http://emailregex.com/ taken from hackernews
EMAIL_RE = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)")
USER_RE = re.compile(r"^[a-zA-Z0-9_-]{3,20}$")
PASS_RE = re.compile(r"^.{3,20}$")
# FILE LEVEL FUNCTIONS
def blog_key(name='default'):
"""
This is the key that defines a single blog and facilitiate multiple
blogs on the same site.
"""
return db.Key.from_path('blogs', name)
def user_key(group='default'):
"""
This is the key that defines a user group and facilitates
multiple groups on the same site.
"""
return db.Key.from_path('users', group)
def post_key(post_id):
"""
This is the key that defines a post and facilitates
multiple groups on the same site.
"""
return db.Key.from_path('Post', int(post_id), parent=blog_key())
def render_str(template, **params):
""" Gets the template and passes it with paramanters. """
tmp = JINJA_ENV.get_template(template)
return tmp.render(params)
def like_dup(ent, login_id, post_id):
key = post_key(post_id)
like_exists = db.GqlQuery("SELECT * "
"FROM " + ent +
" WHERE like_user_id = '" + login_id +
"' AND ANCESTOR IS :1", key).get()
return like_exists
# CLASS DEFINITIONS
class EncryptHandler(object):
""" handles basic encryption functions """
def make_salt(self, salt_length=5):
"""
Creates a salt for salting passwords and other hashed values """
return ''.join(random.choice(letters)
for x in xrange(salt_length))
def hash_pass(self, username, password, salt=None):
"""
if a password salt does not exist create one, otherwise hash the
user data .
"""
if not salt:
salt = self.make_salt()
hashed_pass = hashlib.sha256(username + password + salt).hexdigest()
return '%s|%s' % (salt, hashed_pass)
def valid_pass_hash(self, username, password, hashed_pass):
"""
Checks to see if the password is valid by comparing it to a hash
passed into the function.
"""
salt = hashed_pass.split('|')[0]
return hashed_pass == self.hash_pass(username, password, salt)
def make_secure_val(self, val):
return '%s|%s' % (val, hmac.new(COOKIE_SECRET, val).hexdigest())
def get_secure_val(self, secure_val):
if secure_val:
val = secure_val.split('|')[0]
else:
val = None
if secure_val == self.make_secure_val(val):
return val
class AuthHandler(EncryptHandler):
""" Handles user authentication """
def user_exists(self, username):
""" validates that the user exists in the database """
username_exists = db.GqlQuery("SELECT * "
"FROM User "
"WHERE username = :usernm",
usernm=username).get()
return username_exists
def user_auth(self, username, password):
"""
If the username exists it suthenticates the password of the user
"""
user = db.GqlQuery("SELECT * "
"FROM User "
"WHERE username = :usernm",
usernm=username).get()
if user:
return self.valid_pass_hash(user.username,
password,
user.pass_hash)
class TemplateHandler(webapp2.RequestHandler, EncryptHandler):
"""
TemplateHandler class for all functions in this app for rendering
any templates.
"""
def write(self, *a, **kw):
""" displays the respective function, parameters, ect. """
self.response.out.write(*a, **kw)
def render_tmp(self, template, **params):
"""
Gets the template and passes it with paramanters to a
file level function called render_str.
"""
return render_str(template, **params)
def render(self, template, **kw):
"""
Calls render_tmp and write to display the jinja template.
"""
# Loads the nav for each page from a tuple in the order of link, title.
if self.read_secure_cookie('usercookie'):
# Gets the user id from the cookie
user_id = self.read_secure_cookie('usercookie')
# gets the key for the kind (table)
key = db.Key.from_path('User',
int(user_id),
parent=user_key())
# gets the user data based upon what is passed
# from user_id into key
user = db.get(key)
login_status = "<span>Logged in as: %s </span>" % (user.username)
nav = [('/', 'Home'),
('/newpost', 'Create New Post'),
('/logout', 'Log Out')]
else:
login_status = ''
user_id = ''
nav = [('/', 'Home'),
('/signup', 'Sign Up'),
('/login', 'Log In')]
self.write(self.render_tmp(template, login_id=user_id,
nav=nav, login_status=login_status, **kw))
def set_secure_cookie(self, name, val, exp):
"""
Method takes in a name, value, and expiration it creates a cookie.
The name parameter takes in any value and converts it to a string.
The val parameter takes in any value and converts it to a string.
The exp parameter takes in integer representing the number of
seconds before expiration.
A null or non integer value in the expiration parameter sets a
session cookie.
"""
# sets cookie that expires after a specified time unless refreshed
# a blank value sets a session cookie
cookie_val = self.make_secure_val(str(val))
if exp and isinstance(exp, (int, long, float)):
now = datetime.datetime.utcnow()
expires = datetime.timedelta(seconds=exp)
exp_date = (now + expires).strftime("%a, %d %b %Y %H:%M:%S GMT")
else:
exp_date = ''
self.response.headers.add_header(
'Set-Cookie',
'%s=%s; expires=%s; Path=/' % (name, cookie_val, exp_date))
def read_secure_cookie(self, cookie_name):
"""
Method takes in the name of a cookie and returns its' value.
Remember that the value of a cookie will be in plain text format.
"""
if self.request.cookies.get(cookie_name):
cookie_val = self.request.cookies.get(cookie_name)
val = self.get_secure_val(cookie_val)
return val
else:
return
class Post(db.Model):
"""
Instantiates a class to store post (entity or row) data for posts
(kiind or table) in the datastor consisting of individual atributes
of the post (properties or fields).
"""
author_id = db.StringProperty(required=True)
author_name = db.StringProperty(required=True)
subject = db.StringProperty(required=True)
content = db.TextProperty(required=True)
created = db.DateTimeProperty(auto_now_add=True)
modified = db.DateTimeProperty(auto_now=True)
def post_likes(self, post_id):
# gets the metadata about the datastor
kinds = metadata.get_kinds()
# checks to see if any likes exist and if so displays them
if u'PostLike' in kinds:
likes = db.GqlQuery("SELECT * "
"FROM PostLike "
"WHERE ANCESTOR IS :1",
post_key(post_id)).count()
else:
likes = 0
return likes
def render_post(self, login_id, post_id):
"""
Renders the blog post replacing cariage returns in the text with
html so that it displays correctly in the borowser.
"""
likes = self.post_likes(post_id)
self._render_text = self.content.replace('\n', '<br>')
return render_str("post.html", login_id=login_id,
likes=likes, post=self)
def post_like_dup(self, login_id, post_id):
exists = like_dup('PostLike', login_id, post_id)
return exists
class PostLike(db.Model):
"""
Handles the likes for each of the posts
"""
like_user_id = db.StringProperty(required=True)
class Comment(db.Model):
"""
Instantiates a class to store comments (entity or row) data for comments
(kiind or table) in the datastor consisting of individual atributes
of the post (properties or fields).
"""
author_id = db.StringProperty(required=True)
author_name = db.StringProperty(required=True)
subject = db.StringProperty(required=True)
content = db.TextProperty(required=True)
created = db.DateTimeProperty(auto_now_add=True)
modified = db.DateTimeProperty(auto_now=True)
def render_comment(self, login_id):
"""
Renders the blog post replacing cariage returns in the text with
html so that it displays correctly in the borowser.
"""
self._render_text = self.content.replace('\n', '<br>')
return render_str("comment.html", login_id=login_id,
comment=self)
class User(db.Model):
"""
Instantiates a class to store user (entity or row) data for users
(kiind or table) in the datastor consisting of individual atributes
of the user (properties or fields).
"""
username = db.StringProperty(required=True)
pass_hash = db.StringProperty(required=True)
email = db.StringProperty()
class MainPage(TemplateHandler, AuthHandler):
""" This is the handler class for the main page for the blog. """
def get(self):
"""
queries the database for the 10 most recent
blog posts and orders them descending
"""
posts = db.GqlQuery("SELECT * "
"FROM Post "
"ORDER BY created DESC LIMIT 10")
self.render("front.html", posts=posts)
def post(self):
""" handles the post for from the main page """
auth_error = True
if self.read_secure_cookie('usercookie'):
auth_error = False
else:
auth_error = True
username = self.read_secure_cookie('usercookie')
if not self.user_exists(username):
auth_error = False
else:
auth_error = True
if not auth_error:
edit_post_id = self.request.get('edit_post_id')
comment_post_id = self.request.get('comment_post_id')
like_post_id = self.request.get('like_post_id')
if comment_post_id:
post_id = comment_post_id
self.redirect('/newcomment?post_id=' + post_id)
if edit_post_id:
post_id = edit_post_id
self.redirect('/editpost?post_id=' + post_id)
if like_post_id:
post_id = like_post_id
user_id = self.read_secure_cookie('usercookie')
if not like_dup('PostLike', user_id, post_id):
like = PostLike(like_user_id=user_id,
parent=post_key(post_id))
like.put()
self.redirect('/')
else:
self.redirect('/signup')
class NewPostHandler(TemplateHandler, AuthHandler):
""" This is the handler class for the new blog post page """
def get(self):
"""
uses GET request to render newpost.html by calling render from the
TemplateHandler class
"""
if self.read_secure_cookie('usercookie'):
self.render("newpost.html")
else:
self.redirect('/signup')
def post(self):
"""
handles the POST request from newpost.html
"""
auth_error = True
if self.read_secure_cookie('usercookie'):
auth_error = False
else:
auth_error = True
username = self.read_secure_cookie('usercookie')
if not self.user_exists(username):
auth_error = False
else:
auth_error = True
if not auth_error:
subject_input = self.request.get('subject')
content_input = self.request.get('content')
if self.read_secure_cookie('usercookie'):
# Gets the user id from the cookie if the cookie is set
user_id = self.read_secure_cookie('usercookie')
key = db.Key.from_path('User', int(user_id), parent=user_key())
user = db.get(key)
# if subject, content, and user_id exist create an entity (row) in
# the GAE datastor (database) and redirect to a permanent link to
# the post
if subject_input and content_input and user_id:
post = Post(parent=blog_key(),
author_id=user_id,
author_name=user.username,
subject=subject_input,
content=content_input)
post.put()
# redirects to a single blog post passing the post id
# from the function as a string to a pagewhere the post_id
# is the url
post_id = str(post.key().id())
self.redirect('/post-%s' % post_id)
else:
input_error = "Please submit both the title and content."
self.render("newpost.html", subject=subject_input,
content=content_input,
error=input_error)
else:
self.redirect('/signup')
class NewCommentHandler(TemplateHandler, AuthHandler):
""" This is the handler class for the new blog post page """
def get(self):
"""
uses GET request to render newpost.html by calling render from the
TemplateHandler class
"""
if self.read_secure_cookie('usercookie'):
post_id = self.request.get('post_id')
self.render("newcomment.html", post_id=post_id)
else:
self.redirect('/signup')
def post(self):
"""
handles the POST request from newpost.html
"""
auth_error = True
if self.read_secure_cookie('usercookie'):
auth_error = False
else:
auth_error = True
username = self.read_secure_cookie('usercookie')
if not self.user_exists(username):
auth_error = False
else:
auth_error = True
if not auth_error:
post_id = self.request.get('post_id')
subject_input = self.request.get('subject')
content_input = self.request.get('content')
if self.read_secure_cookie('usercookie'):
# Gets the user id from the cookie if the cookie is set
user_id = self.read_secure_cookie('usercookie')
key = db.Key.from_path('User', int(user_id), parent=user_key())
user = db.get(key)
# if subject, content, and user_id exist create an entity (row) in
# the GAE datastor (database) and redirect to a permanent link to
# the post
if subject_input and content_input and user_id:
comment = Comment(parent=post_key(post_id),
author_id=user_id,
author_name=user.username,
subject=subject_input,
content=content_input)
comment.put()
# redirects to a single blog post passing the post id
# from the function as a string to a pagewhere the post_id
# is the url
comment_id = str(comment.key().id())
self.redirect('/comment-%s?post_id=%s' % (comment_id, post_id))
else:
input_error = "Please submit both the title and content."
self.render("newcomment.html", subject=subject_input,
content=content_input,
error=input_error,
post_id=post_id)
else:
self.redirect('/signup')
class PostLinkHandler(TemplateHandler, AuthHandler):
""" Class to handle successfull blog posts that returns a permalink """
def get(self, login_id):
"""
uses GET request to get the post_id from the new post
and renders permalink.html if the blog post exists by
passing the template into render from the TemplateHandler class.
"""
url_str = self.request.path
post_id = url_str.rsplit('post-', 1)[1]
key = post_key(post_id)
post = db.get(key)
kinds = metadata.get_kinds()
# checks to see if any comments exist and if so displays them
if u'Comment' in kinds:
comments = db.GqlQuery("SELECT * "
"FROM Comment "
"WHERE ANCESTOR IS :1", key)
else:
comments = ''
self.render("postlink.html", post=post,
comments=comments)
def post(self, login_id):
auth_error = True
if self.read_secure_cookie('usercookie'):
auth_error = False
else:
auth_error = True
username = self.read_secure_cookie('usercookie')
if not self.user_exists(username):
auth_error = False
else:
auth_error = True
if not auth_error:
edit_post_id = self.request.get('edit_post_id')
edit_comment_id = self.request.get('edit_comment_id')
comment_post_id = self.request.get('comment_post_id')
like_post_id = self.request.get('like_post_id')
if comment_post_id:
post_id = comment_post_id
self.redirect('/newcomment?post_id=' + post_id)
if edit_post_id:
post_id = edit_post_id
self.redirect('/editpost?post_id=' + post_id)
if edit_comment_id:
url_str = self.request.path
post_id = url_str.rsplit('post-', 1)[1]
comment_id = edit_comment_id
self.redirect('/editcomment?post_id=%s&comment_id=%s' %
(post_id, comment_id))
if like_post_id:
post_id = like_post_id
user_id = self.read_secure_cookie('usercookie')
if not like_dup('PostLike', user_id, post_id):
like = PostLike(like_user_id=user_id,
parent=post_key(post_id))
like.put()
self.redirect('/post-%s' % post_id)
else:
self.redirect('/signup')
class CommentLinkHandler(TemplateHandler, AuthHandler):
""" Class to handle successfull blog posts that returns a permalink """
def get(self, login_id):
"""
uses GET request to get the post_id from the new post
and renders permalink.html if the blog post exists by
passing the template into render from the TemplateHandler class.
"""
post_id = self.request.get('post_id')
url_str = self.request.path
comment_id = url_str.rsplit('comment-', 1)[1]
comment_key = db.Key.from_path('Comment', int(comment_id),
parent=post_key(post_id))
comment = db.get(comment_key)
# gets the metadata about the datastor
# checks to see if any comments exist
self.render("commentlink.html", comment=comment)
def post(self, login_id):
auth_error = True
if self.read_secure_cookie('usercookie'):
auth_error = False
else:
auth_error = True
username = self.read_secure_cookie('usercookie')
if not self.user_exists(username):
auth_error = False
else:
auth_error = True
if not auth_error:
comment_id = self.request.get('edit_comment_id')
post_id = self.request.get('post_id')
if self.read_secure_cookie('usercookie'):
if comment_id and post_id:
self.redirect('/editcomment?comment_id=%s&post_id=%s' %
(comment_id, post_id))
else:
self.redirect('/signup')
class UserSignUpHandler(TemplateHandler, EncryptHandler):
""" This is the hander class for the user sign up page """
# user signup form validation constaccccnts using a string literal regex to
# validate user input
def valid_username(self, username):
""" validates the user id input by passing it through regex """
return username and USER_RE.match(username)
def user_exists(self, username):
""" validates that the user exists in the database """
username_exists = db.GqlQuery("SELECT * "
"FROM User "
"WHERE username = :usernm",
usernm=username).get()
return username_exists
def valid_password(self, password):
""" validates the password input by passing it through regex """
return password and PASS_RE.match(password)
def valid_email(self, email):
""" validates the email input by passing it through regex """
return not email or EMAIL_RE.match(email)
def get(self):
"""
uses GET request to render signup.html by passing signup.html into
render from the TemplateHandler class.
"""
self.render("signup.html")
def post(self):
""" handles the POST request from signup.html """
have_error = False
username = self.request.get('username')
password = self.request.get('password')
verify = self.request.get('verify')
email = self.request.get('email')
# dictionary to store error messages, username and email if not valid
params = dict(username=username, email=email)
# if the username already exists or it is an error
if self.user_exists(username):
params['error_username_exists'] = 'User Exists'
have_error = True
elif not self.valid_username(username):
params['error_username'] = 'Invalid User ID'
have_error = True
# tests for valid password and password match
if not self.valid_password(password):
params['error_password'] = 'Invalid Password'
have_error = True
elif password != verify:
params['error_verify'] = 'Passwords do not Match'
have_error = True
# tests for valid email
if not self.valid_email(email):
params['error_email'] = 'Invalid Email'
have_error = True
# if there is an error re-render signup page
# else render the welcome page
if have_error:
self.render("signup.html", **params)
else:
hashed_pass = self.hash_pass(username, password)
user = User(parent=user_key(),
username=username,
pass_hash=hashed_pass,
email=email)
user.put()
user_id = str(user.key().id())
self.set_secure_cookie('usercookie', user_id, None)
self.redirect('/welcome')
class UserLoginHandler(TemplateHandler, AuthHandler):
""" This is the hander class for the user sign up page """
def get(self):
"""
uses GET request to render signup.html by passing signup.html into
render from the TemplateHandler class.
"""
self.render("login.html")
def post(self):
""" handles the POST request from signup.html """
auth_error = True
username = self.request.get('username')
password = self.request.get('password')
# dictionary to store error messages, username and email if not valid
params = dict(username=username)
# if the username already exists or it is an error
if self.user_exists(username):
auth_error = False
# tests for valid password and password match
if self.user_auth(username, password):
auth_error = False
else:
auth_error = True
params['error_password'] = 'Invalid Password'
else:
auth_error = True
params['error_username'] = 'User Does Not Exist'
# if there is an error re-render signup page
# else render the welcome page
if auth_error:
self.render("login.html", **params)
else:
user = db.GqlQuery("SELECT * "
"FROM User "
"WHERE username = :usernm",
usernm=username).get()
user_id = str(user.key().id())
self.set_secure_cookie('usercookie', user_id, None)
self.redirect('/welcome')
class UserLogoutHandler(TemplateHandler, EncryptHandler):
""" logs user out of the application """
def get(self):
"""
Uses GET request to redirect to signup as well as destroying
the cookie.
"""
# sets the expiration time of the cookie to -1 to destroy it
self.set_secure_cookie('usercookie', '', -1)
self.redirect('/signup')
class WelcomeHandler(TemplateHandler):
""" This is the handler class for the welcome page """
def get(self):
""" handles the GET request for welcome.html """
# if the usercookie exists render the welcome page
# otherwise redirect to the signup page.
if self.read_secure_cookie('usercookie'):
# Gets the user id from the cookie
user_id = self.read_secure_cookie('usercookie')
# gets the key for the kind (table)
key = db.Key.from_path('User',
int(user_id),
parent=user_key())
# gets the user data based upon what is passed
# from user_id into key
user = db.get(key)
# renders the welcome page passing in the user name
self.render("welcome.html",
username=user.username)
else:
self.redirect('/signup')
class EditPost(TemplateHandler, AuthHandler):
""" Handles the editing of blog posts """
def get(self):
""" uses get request to get newpost.html """
post_id = self.request.get('post_id')
key = db.Key.from_path('Post',
int(post_id),
parent=blog_key())
# gets the post data based upon what
# is passed from post_id into key
post = db.get(key)
if self.read_secure_cookie('usercookie'):
user_id = self.read_secure_cookie('usercookie')
# If the current logged in user is not the post author
# it redirects them back to the previous page
if user_id == post.author_id:
self.render("editpost.html",
subject=post.subject,
content=post.content,
post_id=post_id)
else:
referrer = self.request.headers.get('referer')
if referrer:
return self.redirect(referrer)
return self.redirect_to('/')
else:
self.redirect('/signup')
def post(self):
"""
handles the POST request from newpost.html
"""
auth_error = True
if self.read_secure_cookie('usercookie'):
auth_error = False
else:
auth_error = True
username = self.read_secure_cookie('usercookie')
if not self.user_exists(username):
auth_error = False
else:
auth_error = True
if not auth_error:
post_id = self.request.get('post_id')
subject_input = self.request.get('subject')
content_input = self.request.get('content')
post_key = db.Key.from_path('Post',
int(post_id),
parent=blog_key())
# gets the post data based upon what
# is passed from post_id into key
# if subject and content exist create an entity (row) in
# the GAE datastor (database) and redirect to a permanent
# link to the post
if subject_input and content_input:
post = db.get(post_key)
post.subject = subject_input
post.content = content_input
post.put()
# redirects to a single blog post passing the post id
# from the function as a string to a pagewhere the post_id
# is the url
post_id = str(post.key().id())
self.redirect('/post-%s' % post_id)
else:
input_error = "Please submit both the title and content."
self.render("editpost.html", subject=subject_input,
content=content_input,
error=input_error, post_id=post_id)
else:
self.redirect('/signup')
class EditComment(TemplateHandler, AuthHandler):
""" Handles the editing of blog posts """
def get(self):
""" uses get request to get newpost.html """
comment_id = self.request.get('comment_id')
post_id = self.request.get('post_id')
key = db.Key.from_path('Comment',
int(comment_id),
parent=post_key(post_id))
# gets the post data based upon what
# is passed from post_id into key
comment = db.get(key)
if self.read_secure_cookie('usercookie'):
user_id = self.read_secure_cookie('usercookie')
# If the current logged in user is not the post author
# it redirects them back to the previous page
if user_id == comment.author_id:
self.render("editcomment.html",
subject=comment.subject,
content=comment.content,
post_id=post_id,
comment_id=comment_id)
else:
referrer = self.request.headers.get('referer')
if referrer:
return self.redirect(referrer)
return self.redirect_to('/')
else:
self.redirect('/signup')
def post(self):
"""
handles the POST request from newpost.html
"""
auth_error = True
if self.read_secure_cookie('usercookie'):
auth_error = False
else:
auth_error = True
username = self.read_secure_cookie('usercookie')
if not self.user_exists(username):
auth_error = False
else:
auth_error = True
if not auth_error:
post_id = self.request.get('post_id')
comment_id = self.request.get('comment_id')
subject_input = self.request.get('subject')
content_input = self.request.get('content')
comment_key = db.Key.from_path('Comment',
int(comment_id),
parent=post_key(post_id))
# gets the post data based upon what
# is passed from post_id into key
# if subject and content exist create an entity (row) in the GAE
# datastor (database) and redirect to a permanent link to the post
if subject_input and content_input:
comment = db.get(comment_key)
comment.subject = subject_input
comment.content = content_input
comment.put()
# redirects to a single blog post passing the post id
# from the function as a string to a pagewhere the post_id
# is the url
self.redirect('/comment-%s?post_id=%s' % (comment_id, post_id))
else:
input_error = "Please submit both the title and the content."
self.render("editcomment.html", subject=subject_input,
content=content_input, error=input_error,
comment_id=comment_id, post_id=post_id)
else:
self.redirect('/signup')
class DeletePost(TemplateHandler, AuthHandler):
""" This handles the deletion of blog posts """
def post(self):
""" Submits data to the server to delete the post """
auth_error = True
if self.read_secure_cookie('usercookie'):
auth_error = False
else:
auth_error = True
username = self.read_secure_cookie('usercookie')
if not self.user_exists(username):
auth_error = False
else:
auth_error = True
if not auth_error:
post_id = self.request.get('post_id')
key = db.Key.from_path('Post',
int(post_id),
parent=blog_key())
# gets the post data based upon what
# is passed from post_id into key
db.delete(key)
self.render('/postdeleted.html')
else:
self.redirect('/signup')
class DeleteComment(TemplateHandler, AuthHandler):
""" This handles the deletion of blog posts """
def post(self):
""" Submits data to the server to delete the post """
auth_error = True
if self.read_secure_cookie('usercookie'):
auth_error = False
else:
auth_error = True
username = self.read_secure_cookie('usercookie')
if not self.user_exists(username):
auth_error = False
else:
auth_error = True
if not auth_error:
comment_id = self.request.get('comment_id')
post_id = self.request.get('post_id')
key = db.Key.from_path('Comment',
int(comment_id),
parent=post_key(post_id))
# gets the post data based upon what
# is passed from post_id into key
db.delete(key)
self.render('/commentdeleted.html')
else:
self.redirect('/signup')
# GAE APPLICATION VARIABLE
# This variable sets the atributes of the individual HTML
# files that will be served using google app engine.
# '/([0-9]+)' recieves post_id from NewPostHandler class passing it
# into PermaPost class via the url using regular expression
WSGI_APP = webapp2.WSGIApplication([('/?', MainPage),
('/post-([0-9]+)',
PostLinkHandler),
('/comment-([0-9]+)',
CommentLinkHandler),
('/newpost', NewPostHandler),
('/newcomment', NewCommentHandler),
('/signup', UserSignUpHandler),
('/editpost', EditPost),
('/editcomment', EditComment),
('/login', UserLoginHandler),
('/logout', UserLogoutHandler),
('/deletepost', DeletePost),
('/deletecomment', DeleteComment),
('/welcome', WelcomeHandler)
], debug=True)