PageRenderTime 46ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/chapter_3/hw_3_2/blog.py

https://gitlab.com/chuxz777/mongodb-101
Python | 332 lines | 284 code | 22 blank | 26 comment | 8 complexity | 1b3e2cc8397c7a5c9ec92db1b730b373 MD5 | raw file
  1. #
  2. # Copyright (c) 2008 - 2013 10gen, Inc. <http://10gen.com>
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. #
  17. import pymongo
  18. import blogPostDAO
  19. import sessionDAO
  20. import userDAO
  21. import bottle
  22. import cgi
  23. import re
  24. __author__ = 'aje'
  25. # General Discussion on structure. This program implements a blog. This file is the best place to start to get
  26. # to know the code. In this file, which is the controller, we define a bunch of HTTP routes that are handled
  27. # by functions. The basic way that this magic occurs is through the decorator design pattern. Decorators
  28. # allow you to modify a function, adding code to be executed before and after the function. As a side effect
  29. # the bottle.py decorators also put each callback into a route table.
  30. # These are the routes that the blog must handle. They are decorated using bottle.py
  31. # This route is the main page of the blog
  32. @bottle.route('/')
  33. def blog_index():
  34. cookie = bottle.request.get_cookie("session")
  35. username = sessions.get_username(cookie)
  36. # even if there is no logged in user, we can show the blog
  37. l = posts.get_posts(10)
  38. return bottle.template('blog_template', dict(myposts=l, username=username))
  39. # Displays a particular blog post
  40. @bottle.get("/post/<permalink>")
  41. def show_post(permalink="notfound"):
  42. cookie = bottle.request.get_cookie("session")
  43. username = sessions.get_username(cookie)
  44. permalink = cgi.escape(permalink)
  45. print "about to query on permalink = ", permalink
  46. post = posts.get_post_by_permalink(permalink)
  47. if post is None:
  48. bottle.redirect("/post_not_found")
  49. # init comment form fields for additional comment
  50. comment = {'name': "", 'body': "", 'email': ""}
  51. return bottle.template("entry_template", dict(post=post, username=username, errors="", comment=comment))
  52. # used to process a comment on a blog post
  53. @bottle.post('/newcomment')
  54. def post_new_comment():
  55. name = bottle.request.forms.get("commentName")
  56. email = bottle.request.forms.get("commentEmail")
  57. body = bottle.request.forms.get("commentBody")
  58. permalink = bottle.request.forms.get("permalink")
  59. post = posts.get_post_by_permalink(permalink)
  60. cookie = bottle.request.get_cookie("session")
  61. username = sessions.get_username(cookie)
  62. # if post not found, redirect to post not found error
  63. if post is None:
  64. bottle.redirect("/post_not_found")
  65. return
  66. # if values not good, redirect to view with errors
  67. if name == "" or body == "":
  68. # user did not fill in enough information
  69. # init comment for web form
  70. comment = {'name': name, 'email': email, 'body': body}
  71. errors = "Post must contain your name and an actual comment."
  72. return bottle.template("entry_template", dict(post=post, username=username, errors=errors, comment=comment))
  73. else:
  74. # it all looks good, insert the comment into the blog post and redirect back to the post viewer
  75. posts.add_comment(permalink, name, email, body)
  76. bottle.redirect("/post/" + permalink)
  77. @bottle.get("/post_not_found")
  78. def post_not_found():
  79. return "Sorry, post not found"
  80. # Displays the form allowing a user to add a new post. Only works for logged in users
  81. @bottle.get('/newpost')
  82. def get_newpost():
  83. cookie = bottle.request.get_cookie("session")
  84. username = sessions.get_username(cookie) # see if user is logged in
  85. if username is None:
  86. bottle.redirect("/login")
  87. return bottle.template("newpost_template", dict(subject="", body = "", errors="", tags="", username=username))
  88. #
  89. # Post handler for setting up a new post.
  90. # Only works for logged in user.
  91. @bottle.post('/newpost')
  92. def post_newpost():
  93. title = bottle.request.forms.get("subject")
  94. post = bottle.request.forms.get("body")
  95. tags = bottle.request.forms.get("tags")
  96. cookie = bottle.request.get_cookie("session")
  97. username = sessions.get_username(cookie) # see if user is logged in
  98. if username is None:
  99. bottle.redirect("/login")
  100. if title == "" or post == "":
  101. errors = "Post must contain a title and blog entry"
  102. return bottle.template("newpost_template", dict(subject=cgi.escape(title, quote=True), username=username,
  103. body=cgi.escape(post, quote=True), tags=tags, errors=errors))
  104. # extract tags
  105. tags = cgi.escape(tags)
  106. tags_array = extract_tags(tags)
  107. # looks like a good entry, insert it escaped
  108. escaped_post = cgi.escape(post, quote=True)
  109. # substitute some <p> for the paragraph breaks
  110. newline = re.compile('\r?\n')
  111. formatted_post = newline.sub("<p>", escaped_post)
  112. permalink = posts.insert_entry(title, formatted_post, tags_array, username)
  113. # now bottle.redirect to the blog permalink
  114. bottle.redirect("/post/" + permalink)
  115. # displays the initial blog signup form
  116. @bottle.get('/signup')
  117. def present_signup():
  118. return bottle.template("signup",
  119. dict(username="", password="",
  120. password_error="",
  121. email="", username_error="", email_error="",
  122. verify_error =""))
  123. # displays the initial blog login form
  124. @bottle.get('/login')
  125. def present_login():
  126. return bottle.template("login",
  127. dict(username="", password="",
  128. login_error=""))
  129. # handles a login request
  130. @bottle.post('/login')
  131. def process_login():
  132. username = bottle.request.forms.get("username")
  133. password = bottle.request.forms.get("password")
  134. print "user submitted ", username, "pass ", password
  135. user_record = users.validate_login(username, password)
  136. if user_record:
  137. # username is stored in the user collection in the _id key
  138. session_id = sessions.start_session(user_record['_id'])
  139. if session_id is None:
  140. bottle.redirect("/internal_error")
  141. cookie = session_id
  142. # Warning, if you are running into a problem whereby the cookie being set here is
  143. # not getting set on the redirect, you are probably using the experimental version of bottle (.12).
  144. # revert to .11 to solve the problem.
  145. bottle.response.set_cookie("session", cookie)
  146. bottle.redirect("/welcome")
  147. else:
  148. return bottle.template("login",
  149. dict(username=cgi.escape(username), password="",
  150. login_error="Invalid Login"))
  151. @bottle.get('/internal_error')
  152. @bottle.view('error_template')
  153. def present_internal_error():
  154. return {'error':"System has encountered a DB error"}
  155. @bottle.get('/logout')
  156. def process_logout():
  157. cookie = bottle.request.get_cookie("session")
  158. sessions.end_session(cookie)
  159. bottle.response.set_cookie("session", "")
  160. bottle.redirect("/signup")
  161. @bottle.post('/signup')
  162. def process_signup():
  163. email = bottle.request.forms.get("email")
  164. username = bottle.request.forms.get("username")
  165. password = bottle.request.forms.get("password")
  166. verify = bottle.request.forms.get("verify")
  167. # set these up in case we have an error case
  168. errors = {'username': cgi.escape(username), 'email': cgi.escape(email)}
  169. if validate_signup(username, password, verify, email, errors):
  170. if not users.add_user(username, password, email):
  171. # this was a duplicate
  172. errors['username_error'] = "Username already in use. Please choose another"
  173. return bottle.template("signup", errors)
  174. session_id = sessions.start_session(username)
  175. print session_id
  176. bottle.response.set_cookie("session", session_id)
  177. bottle.redirect("/welcome")
  178. else:
  179. print "user did not validate"
  180. return bottle.template("signup", errors)
  181. @bottle.get("/welcome")
  182. def present_welcome():
  183. # check for a cookie, if present, then extract value
  184. cookie = bottle.request.get_cookie("session")
  185. username = sessions.get_username(cookie) # see if user is logged in
  186. if username is None:
  187. print "welcome: can't identify user...redirecting to signup"
  188. bottle.redirect("/signup")
  189. return bottle.template("welcome", {'username': username})
  190. # Helper Functions
  191. #extracts the tag from the tags form element. an experience python programmer could do this in fewer lines, no doubt
  192. def extract_tags(tags):
  193. whitespace = re.compile('\s')
  194. nowhite = whitespace.sub("",tags)
  195. tags_array = nowhite.split(',')
  196. # let's clean it up
  197. cleaned = []
  198. for tag in tags_array:
  199. if tag not in cleaned and tag != "":
  200. cleaned.append(tag)
  201. return cleaned
  202. # validates that the user information is valid for new signup, return True of False
  203. # and fills in the error string if there is an issue
  204. def validate_signup(username, password, verify, email, errors):
  205. USER_RE = re.compile(r"^[a-zA-Z0-9_-]{3,20}$")
  206. PASS_RE = re.compile(r"^.{3,20}$")
  207. EMAIL_RE = re.compile(r"^[\S]+@[\S]+\.[\S]+$")
  208. errors['username_error'] = ""
  209. errors['password_error'] = ""
  210. errors['verify_error'] = ""
  211. errors['email_error'] = ""
  212. if not USER_RE.match(username):
  213. errors['username_error'] = "invalid username. try just letters and numbers"
  214. return False
  215. if not PASS_RE.match(password):
  216. errors['password_error'] = "invalid password."
  217. return False
  218. if password != verify:
  219. errors['verify_error'] = "password must match"
  220. return False
  221. if email != "":
  222. if not EMAIL_RE.match(email):
  223. errors['email_error'] = "invalid email address"
  224. return False
  225. return True
  226. connection_string = "mongodb://localhost"
  227. connection = pymongo.MongoClient(connection_string)
  228. database = connection.blog
  229. posts = blogPostDAO.BlogPostDAO(database)
  230. users = userDAO.UserDAO(database)
  231. sessions = sessionDAO.SessionDAO(database)
  232. bottle.debug(True)
  233. bottle.run(host='localhost', port=8082) # Start the webserver running and wait for requests