Permalink
Browse files

changed to hd-signup, added need/create account flow, fixed some bugs

  • Loading branch information...
1 parent 5b8e1a4 commit 787ff6df733ec4a7860b78e495b7909d9d651a8b @progrium progrium committed Feb 13, 2010
Showing with 243 additions and 15 deletions.
  1. +3 −1 app.yaml
  2. +44 −0 keymaster.py
  3. +127 −7 main.py
  4. +1 −1 static/style.css
  5. +28 −0 templates/account.html
  6. +5 −3 templates/main.html
  7. +31 −0 templates/needaccount.html
  8. +4 −3 templates/success.html
View
@@ -1,4 +1,4 @@
-application: hackerdojo-signup
+application: hd-signup
version: 1
runtime: python
api_version: 1
@@ -12,5 +12,7 @@ handlers:
upload: static/robots.txt
- url: /static
static_dir: static
+- url: /remote_api
+ script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
- url: .*
script: main.py
View
@@ -0,0 +1,44 @@
+from google.appengine.api import urlfetch, memcache
+from google.appengine.ext import webapp
+import urllib
+
+## Example usage
+#
+# def needs_key():
+# key = keymaster.get('my-key')
+# if key:
+# # Do something with key
+# else:
+# keymaster.request('my-key')
+#
+# def main():
+# application = webapp.WSGIApplication([
+# ('/key/(.+)', keymaster.Handler({
+# 'my-key': ('6f7e21711e29e6d4b4e64daceb2a7348', '2isy046g', needs_key),
+# 'another-key': ('keymaster-hash', 'keymaster-secret', optional_key_arrival_callback),
+# })),
+# ], debug=True)
+
+_keys = {}
+
+def get(keyname):
+ return memcache.get(keyname, namespace='keymaster')
+
+def request(keyname):
+ urlfetch.fetch('http://www.thekeymaster.org/%s' % _keys[keyname][0], method='POST', payload=urllib.urlencode({'secret': _keys[keyname][1]}), deadline=10)
+
+class _Handler(webapp.RequestHandler):
+ def get(self, keyname):
+ request(keyname)
+
+ def post(self, keyname):
+ key = self.request.get('key')
+ if key:
+ memcache.set(keyname, key, namespace='keymaster')
+ if len(_keys[keyname]) > 2:
+ _keys[keyname][2]()
+
+def Handler(keys):
+ global _keys
+ _keys = keys
+ return _Handler
View
@@ -2,9 +2,15 @@
import datetime, time, hashlib, urllib, urllib2, re, os
from google.appengine.ext import db
from google.appengine.ext import webapp
-from google.appengine.api import urlfetch, mail
+from google.appengine.api import urlfetch, mail, memcache, users
from google.appengine.ext.webapp import template
+from django.utils import simplejson
+import logging
import spreedly
+import keymaster
+
+APP_NAME = 'hd-signup'
+EMAIL_FROM = "Dojo Signup <no-reply@%s.appspotmail.com>" % APP_NAME
try:
is_dev = os.environ['SERVER_SOFTWARE'].startswith('Dev')
@@ -26,6 +32,18 @@
def build_subscriber_url(account):
return "https://spreedly.com/%s/subscriber_accounts/%s" % (SPREEDLY_ACCOUNT, account.token)
+def fetch_usernames(use_cache=True):
+ usernames = memcache.get('usernames')
+ if usernames and use_cache:
+ return usernames
+ else:
+ resp = urlfetch.fetch('http://hackerdojo-domain.appspot.com/users', deadline=10)
+ if resp.status_code == 200:
+ usernames = [m.lower() for m in simplejson.loads(resp.content)]
+ if not memcache.set('usernames', usernames, 3600*24):
+ logging.error("Memcache set failed.")
+ return usernames
+
class Membership(db.Model):
hash = db.StringProperty()
first_name = db.StringProperty(required=True)
@@ -40,10 +58,16 @@ class Membership(db.Model):
created = db.DateTimeProperty(auto_now_add=True)
updated = db.DateTimeProperty(auto_now=True)
+
+ def full_name(self):
+ return '%s %s' % (self.first_name, self.last_name)
class MainHandler(webapp.RequestHandler):
def get(self):
- self.response.out.write(template.render('templates/main.html', {'is_prod': is_prod, 'plan': self.request.get('plan', 'full')}))
+ self.response.out.write(template.render('templates/main.html', {
+ 'is_prod': is_prod,
+ 'plan': self.request.get('plan', 'full'),
+ 'paypal': self.request.get('paypal')}))
def post(self):
first_name = self.request.get('first_name')
@@ -56,20 +80,86 @@ def post(self):
else:
existing_member = Membership.all().filter('email =', email).get()
if existing_member:
- if existing_member.status == 'unpaid':
+ if existing_member.status == None:
existing_member.delete()
else:
self.response.out.write(template.render('templates/main.html', {'is_prod': is_prod, 'plan': plan, 'message': "You're already in our system!"}))
return
m = Membership(first_name=first_name, last_name=last_name, email=email, plan=plan)
+ if self.request.get('paypal') == '1':
+ m.status = 'paypal'
m.hash = hashlib.md5(m.email).hexdigest()
m.referrer = self.request.get('referrer')
m.put()
id = str(m.key().id())
username = "%s-%s-%s" % (m.first_name.lower(), m.last_name.lower(), id)
- query_str = urllib.urlencode({'first_name': m.first_name, 'last_name': m.last_name, 'email': m.email, 'return_url': 'http://%s/success/%s' % (self.request.host, m.hash)})
+ query_str = urllib.urlencode({'first_name': m.first_name, 'last_name': m.last_name, 'email': m.email, 'return_url': 'http://%s/account/%s' % (self.request.host, m.hash)})
self.redirect("https://spreedly.com/%s/subscribers/%s/subscribe/%s/%s?%s" % (SPREEDLY_ACCOUNT, id, PLAN_IDS[m.plan], username, query_str))
+class AccountHandler(webapp.RequestHandler):
+ def get(self, hash):
+ m = Membership.all().filter('hash =', hash).get()
+ if m.username:
+ self.redirect('/success/%s' % hash)
+ else:
+ user = users.get_current_user()
+ if user:
+ m.username = user.nickname().split('@')[0]
+ m.put()
+ self.redirect(users.create_logout_url('/success/%s' % hash))
+ else:
+ if not keymaster.get('api-secret'):
+ keymaster.request('api-secret')
+ message = self.request.get('message')
+ p = re.compile(r'[^\w]')
+ username = '.'.join([p.sub('', m.first_name), p.sub('', m.last_name)]).lower()
+ if username in fetch_usernames():
+ username = m.email.split('@')[0]
+ if self.request.get('u'):
+ pick_username = True
+ login_url = users.create_login_url(self.request.path)
+ self.response.out.write(template.render('templates/account.html', locals()))
+
+ def post(self, hash):
+ username = self.request.get('username')
+ password = self.request.get('password')
+ if password != self.request.get('password_confirm'):
+ self.redirect(self.request.path + "?message=Passwords don't match")
+ elif len(password) < 6:
+ self.redirect(self.request.path + "?message=Password must be 6 characters or longer")
+ else:
+ if not keymaster.get('api-secret'):
+ self.redirect(self.request.path + "?message=There was a caching error, please try again.")
+ else:
+ m = Membership.all().filter('hash =', hash).get()
+
+ try:
+ resp = urlfetch.fetch('http://hackerdojo-domain.appspot.com/users', method='POST', payload=urllib.urlencode({
+ 'username': username,
+ 'password': password,
+ 'first_name': m.first_name,
+ 'last_name': m.last_name,
+ 'secret': keymaster.get('api-secret'),
+ }), deadline=10)
+ if 'try again' in resp.content:
+ self.redirect(self.request.path + "?message=There was a caching error, please try again.")
+ return
+ except urlfetch.DownloadError:
+ pass
+
+ usernames = fetch_usernames(False)
+ if username in usernames:
+ m.username = username
+ m.put()
+ self.redirect('/success/%s' % hash)
+ else:
+ mail.send_mail(sender=EMAIL_FROM,
+ to="Jeff Lindsay <progrium@gmail.com>",
+ subject="Error creating account",
+ body=resp.content)
+ self.redirect(self.request.path + "?message=There was a problem creating your account. Please contact an admin.")
+
+
class SuccessHandler(webapp.RequestHandler):
def get(self):
success_html = urlfetch.fetch("http://hackerdojo.pbworks.com/api_v2/op/GetPage/page/SubscriptionSuccess/_type/html").content
@@ -79,6 +169,27 @@ def get(self):
is_prod = not is_dev
self.response.out.write(template.render('templates/success.html', locals()))
+class NeedAccountHandler(webapp.RequestHandler):
+ def get(self):
+ message = self.request.get('message')
+ self.response.out.write(template.render('templates/needaccount.html', locals()))
+
+ def post(self):
+ email = self.request.get('email')
+ if not email:
+ self.redirect(self.request.path)
+ else:
+ member = Membership.all().filter('email =', email).get()
+ if not member:
+ self.redirect(self.request.path + '?message=There is no record of that email.')
+ else:
+ mail.send_mail(sender=EMAIL_FROM,
+ to="%s <%s>" % (member.full_name(), member.email),
+ subject="Create your Hacker Dojo account",
+ body="""Hello,\n\nHere's a link to create your Hacker Dojo account:\n\nhttp://%s/account/%s""" % (self.request.host, member.hash))
+ sent = True
+ self.response.out.write(template.render('templates/needaccount.html', locals()))
+
class UpdateHandler(webapp.RequestHandler):
def get(self):
pass
@@ -89,7 +200,11 @@ def post(self, ids=None):
for id in subscriber_ids:
subscriber = s.subscriber_details(sub_id=int(id))
member = Membership.get_by_id(int(subscriber['customer-id']))
- #old_status = member.status
+ if member.status == 'paypal':
+ mail.send_mail(sender=EMAIL_FROM,
+ to="PayPal <paypal@hackerdojo.com>",
+ subject="Please cancel PayPal subscription for %s" % member.full_name(),
+ body=member.email)
member.status = 'active' if subscriber['active'] == 'true' else 'suspended'
member.spreedly_token = subscriber['token']
member.plan = subscriber['feature-level'] or member.plan
@@ -108,7 +223,7 @@ def post(self):
deleted_emails.append(membership.email)
membership.delete()
if deleted_emails:
- mail.send_mail(sender="Signup <no-reply@hackerdojo-signup.appspotmail.com>",
+ mail.send_mail(sender=EMAIL_FROM,
to="Jeff Lindsay <progrium@gmail.com>",
subject="Recent almost members",
body='\n'.join(deleted_emails))
@@ -117,8 +232,13 @@ def main():
application = webapp.WSGIApplication([
('/', MainHandler),
('/cleanup', CleanupHandler),
+ ('/account/(.+)', AccountHandler),
+ ('/upgrade/needaccount', NeedAccountHandler),
('/success.*', SuccessHandler),
- ('/update', UpdateHandler),], debug=True)
+ ('/update', UpdateHandler),
+ ('/key/(.+)', keymaster.Handler({
+ 'api-secret': ('c94d981ca589581cd270439854f08679', '1w5q7v3h'),
+ })),], debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
View
@@ -10,7 +10,7 @@ body { font-family: Verdana,Arial,sans-serif; }
.step.inactive span { background-color: gray;}
.step.inactive { color: gray; }
-h4 { margin-bottom: 0px;}
+h4 { margin-bottom: 0px; padding-left: 50px;}
table { margin-top: 50px;}
table .label { text-align: right; width: 210px;}
@@ -0,0 +1,28 @@
+{% extends 'base.html' %}
+{% block content %}
+<h4>
+ <span class="step inactive"><span>1</span> Member Info &rarr; </span>
+ <span class="step inactive"><span>2</span> Billing Info &rarr; </span>
+ <span class="step"><span>3</span> Create Account &rarr; </span>
+ <span class="step inactive"><span>4</span> Done!</span>
+</h4>
+<div style="margin-left: 50px; padding-top: 10px;">
+ <form action="{{self.request.path}}" method="post">
+ <table>
+ {% if message %}<tr><td>&nbsp;</td><td style="color: red; font-size: smaller;">{{ message }}</td></tr>{% endif %}
+ <tr><td class="label">Username:</td><td>{% if pick_username %}<input type="text" name="username" value="{{username}}" />{% else %}<strong>{{ username }}</strong><input type="hidden" name="username" value="{{username}}" />{% endif %} @hackerdojo.com</td></tr>
+ <tr><td class="label">Password:</td><td><input type="password" name="password" /></td></tr>
+ <tr><td class="label">Confirm:</td><td><input type="password" name="password_confirm" /></td></tr>
+ </table>
+ <div style="font-size: smaller; margin-left: 216px;">
+ <input type="submit" value="Finish &rarr;" />
+ <br />
+ <br />
+ <a href="{{ login_url }}">Already have a user account?</a>
+ </div>
+
+
+ </form>
+
+</div>
+{% endblock %}
View
@@ -1,13 +1,15 @@
{% extends 'base.html' %}
{% block content %}
<h4>
- <span class="step"><span>1</span> Member Information &rarr; </span>
- <span class="step inactive"><span>2</span> Billing Information &rarr; </span>
- <span class="step inactive"><span>3</span> Welcome to the Dojo!</span>
+ <span class="step"><span>1</span> Member Info &rarr; </span>
+ <span class="step inactive"><span>2</span> Billing Info &rarr; </span>
+ <span class="step inactive"><span>3</span> Create Account &rarr; </span>
+ <span class="step inactive"><span>4</span> Done!</span>
</h4>
<form action="/" method="post">
<input type="hidden" name="plan" value="{{ plan }}" />
+ {% if paypal %}<input type="hidden" name="paypal" value="1" />{% endif %}
<table>
<tr><td class="label">First name:</td><td><input type="text" name="first_name" /></td></tr>
<tr><td class="label">Last name:</td><td><input type="text" name="last_name" /></td></tr>
@@ -0,0 +1,31 @@
+{% extends 'base.html' %}
+{% block content %}
+<center>
+{% if sent %}
+We've sent you a link to create your account. Check your inbox!
+{% else %}
+<h4 style="margin-bottom: 20px;">
+ Looks like you need a Hacker Dojo user account.
+</h4>
+<span style="color: red;">{{ message }}</span>
+<div>
+ <div style="width: 45%; background: #fee; border: 2px solid #db002a; float: left; margin: 5px; padding: 10px;">
+ <h3>I signed up with PayPal.</h3>
+ <p style="font-size:smaller;">You need to upgrade to our new billing system by signing up on this site. Your PayPal subscription will be canceled once you switch.</p>
+ <input type="submit" value="Continue here" style="font-size: larger;" onclick="window.location.href='/?paypal=1'"/>
+ </div>
+
+ <div style="width: 45%; background: #fee; border: 2px solid #db002a; float: right; margin: 5px; padding: 10px;">
+ <h3>I signed up with Spreedly.</h3>
+ <p style="font-size: smaller;">Tell us the email address you used:</p>
+ <form action="{{self.request.path}}" method="post" style="margin:0px;">
+ <input type="text" name="email" style="font-size: larger;"/><br />
+ <input type="submit" value="Continue here" style="font-size: larger;"/>
+ </form>
+ </div>
+
+ <div style="padding-top: 200px;"><a href="/">Not yet a member of Hacker Dojo?</a></div>
+</div>
+{% endif %}
+</center>
+{% endblock %}
@@ -1,9 +1,10 @@
{% extends 'base.html' %}
{% block content %}
<h4>
- <span class="step inactive"><span>1</span> Member Information &rarr; </span>
- <span class="step inactive"><span>2</span> Billing Information &rarr; </span>
- <span class="step"><span>3</span> Welcome to the Dojo!</span>
+ <span class="step inactive"><span>1</span> Member Info &rarr; </span>
+ <span class="step inactive"><span>2</span> Billing Info &rarr; </span>
+ <span class="step inactive"><span>3</span> Create Account &rarr; </span>
+ <span class="step"><span>4</span> Done!</span>
</h4>
<div style="margin-left: 50px; padding-top: 10px;">
{{ success_html }}

0 comments on commit 787ff6d

Please sign in to comment.