Advertisement
Guest User

Untitled

a guest
Aug 27th, 2014
165
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.20 KB | None | 0 0
  1. # This is my Wiki file
  2.  
  3. import os
  4. import re
  5. import random
  6. import hashlib
  7. import hmac
  8. import logging
  9. import json
  10. from datetime import datetime, timedelta
  11. from string import letters
  12. import time
  13.  
  14. import webapp2
  15. import jinja2
  16.  
  17. from google.appengine.api import memcache
  18. from google.appengine.ext import db
  19.  
  20. template_dir = os.path.join(os.path.dirname(__file__), 'templates')
  21. jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir),
  22.                                autoescape = True)
  23.  
  24. secret = 'fart'
  25.  
  26. def make_secure_val(val):
  27.     return '%s|%s' % (val, hmac.new(secret, val).hexdigest())
  28.  
  29. def check_secure_val(secure_val):
  30.     val = secure_val.split('|')[0]
  31.     if secure_val == make_secure_val(val):
  32.         return val
  33.  
  34. def render_str(template, **params):
  35.     t = jinja_env.get_template(template)
  36.     return t.render(params)
  37.  
  38. class WikiHandler(webapp2.RequestHandler):
  39.  
  40.     def write(self, *a, **kw):
  41.         self.response.write(*a, **kw)
  42.  
  43.     def render_str(self, template, **params):
  44.         params['user'] = self.user
  45.         t = jinja_env.get_template(template)
  46.         return t.render(params)
  47.  
  48.     def render(self, template, **kw):
  49.         self.write(self.render_str(template, **kw))
  50.  
  51.     def render_json(self, d):
  52.         json_txt = json.dumps(d)
  53.         self.response.headers['Content-Type'] = 'application/json; charset=UTF-8'
  54.         self.write(json_txt)
  55.  
  56.     def set_secure_cookie(self, name, val):
  57.         cookie_val = make_secure_val(val)
  58.         self.response.headers.add_header(
  59.             'Set-Cookie',
  60.             '%s=%s; Path=/' % (name, cookie_val))
  61.  
  62.     def read_secure_cookie(self, name):
  63.         cookie_val = self.request.cookies.get(name)
  64.         return cookie_val and check_secure_val(cookie_val)
  65.  
  66.     def login(self, user):
  67.         self.set_secure_cookie('user_id', str(user.key().id()))
  68.  
  69.     def logout(self):
  70.         self.response.headers.add_header('Set-Cookie', 'user_id=; Path=/')
  71.  
  72.     def initialize(self, *a, **kw):
  73.         webapp2.RequestHandler.initialize(self, *a, **kw)
  74.         uid = self.read_secure_cookie('user_id')
  75.         self.user = uid and User.by_id(int(uid))
  76.  
  77.         if self.request.url.endswith('.json'):
  78.             self.format = 'json'
  79.         else:
  80.             self.format = 'html'
  81.  
  82. ################# User stuff
  83. def make_salt(length = 5):
  84.     return ''.join(random.choice(letters) for x in xrange(length))
  85.  
  86. def make_pw_hash(name, pw, salt = None):
  87.     if not salt:
  88.         salt = make_salt()
  89.     h = hashlib.sha256(name + pw + salt).hexdigest()
  90.     return '%s,%s' % (salt, h)
  91.  
  92. def valid_pw(name, password, h):
  93.     salt = h.split(',')[0]
  94.     return h == make_pw_hash(name, password, salt)
  95.  
  96. def users_key(group = 'default'):
  97.     return db.Key.from_path('users', group)
  98.  
  99. class User(db.Model):
  100.     name = db.StringProperty(required = True)
  101.     pw_hash = db.StringProperty(required = True)
  102.     email = db.StringProperty()
  103.  
  104.     @classmethod
  105.     def by_id(cls, uid):
  106.         return User.get_by_id(uid, parent = users_key())
  107.  
  108.     @classmethod
  109.     def by_name(cls, name):
  110.         u = User.all().filter('name =', name).get()
  111.         return u
  112.  
  113.     @classmethod
  114.     def register(cls, name, pw, email = None):
  115.         pw_hash = make_pw_hash(name, pw)
  116.         return User(parent = users_key(),
  117.                     name = name,
  118.                     pw_hash = pw_hash,
  119.                     email = email)
  120.  
  121.     @classmethod
  122.     def login(cls, name, pw):
  123.         u = cls.by_name(name)
  124.         if u and valid_pw(name, pw, u.pw_hash):
  125.             return u
  126.  
  127. # This handler called WikiFront is not needed for the project; I used
  128. # it to help me understand templates better.
  129. #class WikiFront(WikiHandler):
  130. #    def get(self):
  131. #        if self.format == 'html':
  132. #            self.render('viewable.html')
  133.  
  134. class Login(WikiHandler):
  135.     def get(self):
  136.         self.render('login-form.html')
  137.  
  138.     def post(self):
  139.         username = self.request.get('username')
  140.         password = self.request.get('password')
  141.  
  142.         u = User.login(username, password)
  143.         if u:
  144.             self.login(u)
  145.             self.redirect('/')
  146.         else:
  147.             msg = 'Invalid login'
  148.             self.render('login-form.html', error = msg)
  149.  
  150. class Logout(WikiHandler):
  151.     def get(self):
  152.         self.logout()
  153.         self.redirect('/')
  154.  
  155. USER_RE = re.compile(r"^[a-zA-Z0-9_-]{3,20}$")
  156. def valid_username(username):
  157.     return username and USER_RE.match(username)
  158.  
  159. PASS_RE = re.compile(r"^.{3,20}$")
  160. def valid_password(password):
  161.     return password and PASS_RE.match(password)
  162.  
  163. EMAIL_RE = re.compile(r'^[\S]+@[\S]+\.[\S]+$')
  164. def valid_email(email):
  165.     return not email or EMAIL_RE.match(email)
  166.  
  167. class Signup(WikiHandler):
  168.     def get(self):
  169.         self.render('signup-form.html')
  170.  
  171.     def post(self):
  172.         have_error = False
  173.         self.username = self.request.get('username')
  174.         self.password = self.request.get('password')
  175.         self.verify = self.request.get('verify')
  176.         self.email = self.request.get('email')
  177.  
  178.         params = dict(username = self.username,
  179.                       email = self.email)
  180.  
  181.         if not valid_username(self.username):
  182.             params['error_username'] = "That's not a valid username."
  183.             have_error = True
  184.  
  185.         if not valid_password(self.password):
  186.             params['error_password'] = "That wasn't a valid password."
  187.             have_error = True
  188.  
  189.         elif self.password != self.verify:
  190.             params['error_verify'] = "Your passwords didn't match."
  191.             have_error = True
  192.  
  193.         if not valid_email(self.email):
  194.             params['error_email'] = "That's not a valid email."
  195.             have_error = True
  196.  
  197.         if have_error:
  198.             self.render('signup-form.html', **params)
  199.         else:
  200.             self.done()
  201.  
  202.     def done(self):
  203.         #make sure the user doesn't already exist
  204.         u = User.by_name(self.username)
  205.         if u:
  206.             msg = 'That user already exists.'
  207.             self.render('signup-form.html', error_username = msg)
  208.         else:
  209.             u = User.register(self.username, self.password, self.email)
  210.             u.put()
  211.  
  212.             self.login(u)
  213.             self.redirect('/')
  214.  
  215.  
  216.  
  217. ################### Refactor this blog post DB class to make it a wikipage one
  218. #def blog_key(name = 'default'):
  219. #    return db.Key.from_path('blogs', name)
  220.  
  221. class WikiArticle(db.Model):
  222.     path = db.StringProperty(required = True)
  223.     content = db.TextProperty(required = True)
  224.     created = db.DateTimeProperty(auto_now_add = True)
  225.     def render(self):
  226.         self._render_text = self.content.replace('\n', '<br>')
  227.         return render_str("post.html", w = self)
  228.  
  229.     def as_dict(self):
  230.         time_fmt = '%c'
  231.         d = {'path': self.path,
  232.              'content': self.content,
  233.              'created': self.created.strftime(time_fmt)}
  234.         return d
  235.  
  236. class EditPage(WikiHandler):
  237.     def get(self, wikipath):
  238.         if self.user:
  239.             self.render("editable.html")
  240.         else:
  241.             self.redirect("/login")
  242.  
  243.     def post(self, wikipath):
  244.         if not self.user:
  245.             self.redirect('/login')
  246.  
  247.         raw_path = self.request.path
  248.         path = raw_path[6:] # Slice the '/_edit' off the front of the path
  249.         content = self.request.get('content')
  250.  
  251.         if path and content:
  252.             w = WikiArticle(path = path, content = content)
  253.             w.put()
  254.             self.redirect(path)
  255.  
  256.        # TODO - Implement error handling, like if the user clicks SAVE with no
  257.        #        content in the textarea.
  258.  
  259.        # else:
  260.         #    self.redirect("/login")
  261.  
  262.       #  if path and content:
  263.         #    w = WikiPage(parent = blog_key(), path = path, content = content)
  264.         #    p.put()
  265.          #   time.sleep(1)
  266.             # Redirect to the viewable wikpage of the same wikiarticle
  267.           #  self.redirect('/%s' % path)
  268.        # else:
  269.             ###### Figure out what to change this else block to
  270.            # error = "subject and content, please!"
  271.           #  self.render("newpost.html", subject = subject, content = content, error = error)
  272.  
  273. class WikiPage(WikiHandler):
  274.     def get(self, wikipath):
  275.         url_path = self.request.path
  276.         wikiarticle = db.GqlQuery("SELECT * FROM WikiArticle WHERE path = url_path")
  277.        
  278.         if wikiarticle:
  279.             self.response.write("yup, got it!")
  280.  
  281.         else:
  282.            self.response.write("Sorry, try again")
  283.  
  284.  
  285.         # Check to see if the path already exists in the DB
  286.         # If path exists:
  287.             # Render viewable version of wikipage(guest vs. user addressed in template)
  288.         # If path doesn't exist:
  289.             # If user:
  290.                 # Redirect to /_edit + PAGE_RE
  291.             # else:
  292.                 # Redirect to login page
  293.        
  294.        
  295.  
  296. PAGE_RE = r'(/(?:[a-zA-Z0-9_-]+/?)*)'
  297. app = webapp2.WSGIApplication([
  298.                                ('/login', Login),
  299.                                ('/signup', Signup),
  300.                                ('/logout', Logout),
  301.                                ('/_edit' + PAGE_RE, EditPage),
  302.                                (PAGE_RE, WikiPage)
  303.                                ], debug=True)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement