Finished remake of signup page. You can now click "signup" for non-free accounts even if you're not logged in.
This commit is contained in:
parent
e9d2ea28d1
commit
c452408106
|
@ -56,9 +56,10 @@ class Root( object ):
|
|||
note_title = unicode,
|
||||
invite_id = Valid_id( none_okay = True ),
|
||||
after_login = Valid_string( min = 0, max = 100 ),
|
||||
plan = Valid_int( none_okay = True ),
|
||||
user_id = Valid_id( none_okay = True ),
|
||||
)
|
||||
def default( self, note_title, invite_id = None, after_login = None, user_id = None ):
|
||||
def default( self, note_title, invite_id = None, after_login = None, plan = None, user_id = None ):
|
||||
"""
|
||||
Convenience method for accessing a note in the main notebook by name rather than by note id.
|
||||
|
||||
|
@ -68,6 +69,8 @@ class Root( object ):
|
|||
@param invite_id: id of the invite used to get to this note (optional)
|
||||
@type after_login: unicode
|
||||
@param after_login: URL to redirect to after login (optional, must start with "/")
|
||||
@type plan: int
|
||||
@param plan: rate plan index (optional, defaults to None)
|
||||
@rtype: unicode
|
||||
@return: rendered HTML page
|
||||
"""
|
||||
|
@ -81,6 +84,8 @@ class Root( object ):
|
|||
return dict( redirect = u"%s/%s?invite_id=%s" % ( https_url, note_title, invite_id ) )
|
||||
if after_login:
|
||||
return dict( redirect = u"%s/%s?after_login=%s" % ( https_url, note_title, after_login ) )
|
||||
if plan:
|
||||
return dict( redirect = u"%s/%s?plan=%s" % ( https_url, note_title, plan ) )
|
||||
else:
|
||||
return dict( redirect = u"%s/%s" % ( https_url, note_title ) )
|
||||
|
||||
|
@ -100,6 +105,8 @@ class Root( object ):
|
|||
result[ "invite_id" ] = invite_id
|
||||
if after_login and after_login.startswith( u"/" ):
|
||||
result[ "after_login" ] = after_login
|
||||
if plan:
|
||||
result[ "signup_plan" ] = plan
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from model.Note import Note
|
|||
from model.Password_reset import Password_reset
|
||||
from model.Invite import Invite
|
||||
from Expose import expose
|
||||
from Validate import validate, Valid_string, Valid_bool, Validation_error
|
||||
from Validate import validate, Valid_string, Valid_bool, Valid_int, Validation_error
|
||||
from Database import Valid_id, end_transaction
|
||||
from Expire import strongly_expire
|
||||
from view.Json import Json
|
||||
|
@ -21,6 +21,7 @@ from view.Blank_page import Blank_page
|
|||
from view.Thanks_note import Thanks_note
|
||||
from view.Thanks_error_note import Thanks_error_note
|
||||
from view.Processing_note import Processing_note
|
||||
from view.Form_submit_page import Form_submit_page
|
||||
|
||||
|
||||
USERNAME_PATTERN = re.compile( "^[a-zA-Z0-9]+$" )
|
||||
|
@ -205,8 +206,9 @@ class Users( object ):
|
|||
email_address = ( Valid_string( min = 0, max = 60 ) ),
|
||||
signup_button = unicode,
|
||||
invite_id = Valid_id( none_okay = True ),
|
||||
rate_plan = Valid_int( none_okay = True ),
|
||||
)
|
||||
def signup( self, username, password, password_repeat, email_address, signup_button, invite_id = None ):
|
||||
def signup( self, username, password, password_repeat, email_address, signup_button, invite_id = None, rate_plan = None ):
|
||||
"""
|
||||
Create a new User based on the given information. Start that user with their own Notebook and a
|
||||
"welcome to your wiki" Note. For convenience, login the newly created user as well.
|
||||
|
@ -223,6 +225,9 @@ class Users( object ):
|
|||
@param signup_button: ignored
|
||||
@type invite_id: unicode
|
||||
@param invite_id: id of invite to redeem upon signup (optional)
|
||||
@type rate_plan: int
|
||||
@param rate_plan: index of rate plan to signup for (optional). if greater than zero, redirect
|
||||
to PayPal subscribe page after signup
|
||||
@rtype: json dict
|
||||
@return: { 'redirect': url, 'authenticated': userdict }
|
||||
@raise Signup_error: passwords don't match or the username is unavailable
|
||||
|
@ -275,6 +280,9 @@ class Users( object ):
|
|||
|
||||
self.convert_invite_to_access( invite, user_id )
|
||||
redirect = u"/notebooks/%s" % invite.notebook_id
|
||||
# if there's a requested rate plan, then redirect to the PayPal subscribe page
|
||||
elif rate_plan and rate_plan > 0:
|
||||
redirect = u"/users/subscribe?rate_plan=%s" % rate_plan
|
||||
# otherwise, just redirect to the newly created notebook
|
||||
else:
|
||||
redirect = u"/notebooks/%s" % notebook.object_id
|
||||
|
@ -284,6 +292,39 @@ class Users( object ):
|
|||
authenticated = user,
|
||||
)
|
||||
|
||||
@expose( view = Form_submit_page )
|
||||
@grab_user_id
|
||||
@validate(
|
||||
rate_plan = Valid_int(),
|
||||
user_id = Valid_id(),
|
||||
)
|
||||
def subscribe( self, rate_plan, user_id ):
|
||||
"""
|
||||
Submit a subscription form to PayPal, allowing the user to subscribe to the given rate plan.
|
||||
|
||||
@type rate_plan: int
|
||||
@param rate_plan: index of rate plan to subscribe to
|
||||
@type user_id: unicode
|
||||
@param user_id: id of current logged-in user
|
||||
@rtype: dict
|
||||
@return: { 'form': subscription_form_html }
|
||||
@raise Signup_error: invalid rate plan, no logged-in user, or missing subscribe button
|
||||
"""
|
||||
if rate_plan == 0 or rate_plan >= len( self.__rate_plans ):
|
||||
raise Signup_error( u"The rate plan is invalid." )
|
||||
|
||||
plan = self.__rate_plans[ rate_plan ]
|
||||
button = plan.get( u"button" )
|
||||
if not button or not button.strip():
|
||||
raise Signup_error(
|
||||
u"Sorry, that rate plan is not configured for subscriptions. Please contact %s." % \
|
||||
( self.__support_email or u"support" )
|
||||
)
|
||||
|
||||
return dict(
|
||||
form = button % user_id,
|
||||
)
|
||||
|
||||
@expose()
|
||||
@end_transaction
|
||||
@grab_user_id
|
||||
|
|
|
@ -142,12 +142,14 @@ class Valid_int( object ):
|
|||
"""
|
||||
Validator for an integer value.
|
||||
"""
|
||||
def __init__( self, min = None, max = None ):
|
||||
def __init__( self, min = None, max = None, none_okay = False ):
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.message = None
|
||||
self.__none_okay = none_okay
|
||||
|
||||
def __call__( self, value ):
|
||||
if self.__none_okay and value in ( None, "None", "" ): return None
|
||||
value = int( value )
|
||||
|
||||
if self.min is not None and value < self.min:
|
||||
|
|
|
@ -238,6 +238,21 @@ class Test_root( Test_controller ):
|
|||
assert result.get( u"after_login" ) is None
|
||||
assert result[ u"user" ].object_id == self.anonymous.object_id
|
||||
|
||||
def test_default_with_plan( self ):
|
||||
plan = u"17"
|
||||
|
||||
result = self.http_get(
|
||||
"/my_note?plan=%s" % plan,
|
||||
)
|
||||
|
||||
assert result
|
||||
assert result[ u"notes" ]
|
||||
assert len( result[ u"notes" ] ) == 1
|
||||
assert result[ u"notes" ][ 0 ].object_id == self.anon_note.object_id
|
||||
assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
|
||||
assert result[ u"signup_plan" ] == 17
|
||||
assert result[ u"user" ].object_id == self.anonymous.object_id
|
||||
|
||||
def test_default_after_login( self ):
|
||||
self.login()
|
||||
|
||||
|
|
|
@ -93,6 +93,18 @@ class Test_users( Test_controller ):
|
|||
|
||||
assert result[ u"redirect" ].startswith( u"/notebooks/" )
|
||||
|
||||
def test_signup_with_rate_plan( self ):
|
||||
result = self.http_post( "/users/signup", dict(
|
||||
username = self.new_username,
|
||||
password = self.new_password,
|
||||
password_repeat = self.new_password,
|
||||
email_address = self.new_email_address,
|
||||
signup_button = u"sign up",
|
||||
rate_plan = u"2",
|
||||
) )
|
||||
|
||||
assert result[ u"redirect" ] == u"/users/subscribe?rate_plan=2"
|
||||
|
||||
def test_signup_without_email_address( self ):
|
||||
result = self.http_post( "/users/signup", dict(
|
||||
username = self.new_username,
|
||||
|
@ -250,6 +262,62 @@ class Test_users( Test_controller ):
|
|||
assert rate_plan[ u"name" ] == u"super"
|
||||
assert rate_plan[ u"storage_quota_bytes" ] == 1337 * 10
|
||||
|
||||
def test_current_after_signup_with_rate_plan( self ):
|
||||
result = self.http_post( "/users/signup", dict(
|
||||
username = self.new_username,
|
||||
password = self.new_password,
|
||||
password_repeat = self.new_password,
|
||||
email_address = self.new_email_address,
|
||||
signup_button = u"sign up",
|
||||
rate_plan = u"2",
|
||||
) )
|
||||
session_id = result[ u"session_id" ]
|
||||
|
||||
assert result[ u"redirect" ] == u"/users/subscribe?rate_plan=2"
|
||||
|
||||
user = self.database.last_saved_obj
|
||||
assert isinstance( user, User )
|
||||
result = cherrypy.root.users.current( user.object_id )
|
||||
|
||||
assert result[ u"user" ].object_id == user.object_id
|
||||
assert result[ u"user" ].username == self.new_username
|
||||
assert result[ u"user" ].email_address == self.new_email_address
|
||||
|
||||
notebooks = result[ u"notebooks" ]
|
||||
notebook = notebooks[ 0 ]
|
||||
assert notebook.object_id
|
||||
assert notebook.revision
|
||||
assert notebook.name == u"my notebook"
|
||||
assert notebook.trash_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
notebook = notebooks[ 1 ]
|
||||
assert notebook.object_id == notebooks[ 0 ].trash_id
|
||||
assert notebook.revision
|
||||
assert notebook.name == u"trash"
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.read_write == True
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == None
|
||||
|
||||
notebook = notebooks[ 2 ]
|
||||
assert notebook.object_id == self.anon_notebook.object_id
|
||||
assert notebook.revision == self.anon_notebook.revision
|
||||
assert notebook.name == self.anon_notebook.name
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.read_write == False
|
||||
assert notebook.owner == False
|
||||
assert notebook.rank == None
|
||||
|
||||
assert result.get( u"login_url" ) is None
|
||||
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
|
||||
|
||||
rate_plan = result[ u"rate_plan" ]
|
||||
assert rate_plan[ u"name" ] == u"super"
|
||||
assert rate_plan[ u"storage_quota_bytes" ] == 1337 * 10
|
||||
|
||||
def test_signup_with_different_passwords( self ):
|
||||
result = self.http_post( "/users/signup", dict(
|
||||
username = self.new_username,
|
||||
|
@ -261,6 +329,58 @@ class Test_users( Test_controller ):
|
|||
|
||||
assert result[ u"error" ]
|
||||
|
||||
def test_subscribe( self ):
|
||||
self.login()
|
||||
|
||||
result = self.http_post( "/users/subscribe", dict(
|
||||
rate_plan = u"1",
|
||||
), session_id = self.session_id )
|
||||
|
||||
form = result.get( u"form" )
|
||||
plan = self.settings[ u"global" ][ u"luminotes.rate_plans" ][ 1 ]
|
||||
|
||||
assert form == plan[ u"button" ] % self.user.object_id
|
||||
|
||||
def test_subscribe_with_free_rate_plan( self ):
|
||||
self.login()
|
||||
|
||||
result = self.http_post( "/users/subscribe", dict(
|
||||
rate_plan = u"0",
|
||||
), session_id = self.session_id )
|
||||
|
||||
assert u"plan" in result[ u"error" ]
|
||||
assert u"invalid" in result[ u"error" ]
|
||||
|
||||
def test_subscribe_with_invalid_rate_plan( self ):
|
||||
self.login()
|
||||
|
||||
result = self.http_post( "/users/subscribe", dict(
|
||||
rate_plan = u"17",
|
||||
), session_id = self.session_id )
|
||||
|
||||
assert u"plan" in result[ u"error" ]
|
||||
assert u"invalid" in result[ u"error" ]
|
||||
|
||||
def test_subscribe_without_login( self ):
|
||||
result = self.http_post( "/users/subscribe", dict(
|
||||
rate_plan = u"1",
|
||||
) )
|
||||
|
||||
assert u"user" in result[ u"error" ]
|
||||
assert u"invalid" in result[ u"error" ]
|
||||
|
||||
def test_subscribe_without_subscribe_button( self ):
|
||||
self.login()
|
||||
self.settings[ u"global" ][ u"luminotes.rate_plans" ][ 1 ][ u"button" ] = u" "
|
||||
|
||||
result = self.http_post( "/users/subscribe", dict(
|
||||
rate_plan = u"1",
|
||||
), session_id = self.session_id )
|
||||
|
||||
|
||||
print result
|
||||
assert u"not configured" in result[ u"error" ]
|
||||
|
||||
def test_demo( self ):
|
||||
result = self.http_post( "/users/demo", dict() )
|
||||
|
||||
|
|
|
@ -348,6 +348,7 @@
|
|||
|
||||
.upgrade_left_area {
|
||||
width: 400px;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
@ -17,6 +17,7 @@ function Wiki( invoker ) {
|
|||
this.invites = evalJSON( getElement( "invites" ).value );
|
||||
this.invite_id = getElement( "invite_id" ).value;
|
||||
this.after_login = getElement( "after_login" ).value;
|
||||
this.signup_plan = getElement( "signup_plan" ).value;
|
||||
this.font_size = null;
|
||||
|
||||
var total_notes_count_node = getElement( "total_notes_count" );
|
||||
|
@ -693,10 +694,12 @@ Wiki.prototype.create_editor = function ( id, note_text, deleted_from_id, revisi
|
|||
connect( editor, "invites_updated", function ( invites ) { self.invites = invites; self.share_notebook(); } );
|
||||
connect( editor, "submit_form", function ( url, form, callback ) {
|
||||
var args = {}
|
||||
if ( url == "/users/signup" || url == "/users/login" ) {
|
||||
if ( url == "/users/signup" ) {
|
||||
args[ "invite_id" ] = self.invite_id;
|
||||
if ( url == "/users/login" )
|
||||
args[ "after_login" ] = self.after_login;
|
||||
args[ "rate_plan" ] = self.signup_plan;
|
||||
} else if ( url == "/users/login" ) {
|
||||
args[ "invite_id" ] = self.invite_id;
|
||||
args[ "after_login" ] = self.after_login;
|
||||
}
|
||||
|
||||
self.invoker.invoke( url, "POST", args, callback, form );
|
||||
|
|
16
view/Form_submit_page.py
Normal file
16
view/Form_submit_page.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from Tags import Html, Head, Body, Script
|
||||
|
||||
|
||||
class Form_submit_page( Html ):
|
||||
def __init__( self, form ):
|
||||
Html.__init__(
|
||||
self,
|
||||
Head(),
|
||||
Body(
|
||||
form,
|
||||
Script( # auto-submit the form
|
||||
u"document.forms[ 0 ].submit();",
|
||||
type = u"text/javascript",
|
||||
),
|
||||
),
|
||||
)
|
|
@ -132,7 +132,7 @@ class Front_page( Product_page ):
|
|||
separator = u"",
|
||||
),
|
||||
Div(
|
||||
u"-Scott Tiner",
|
||||
u"-Scott Tiner, Technical Writer",
|
||||
class_ = u"quote_signature"
|
||||
),
|
||||
class_ = u"quote",
|
||||
|
|
|
@ -32,6 +32,7 @@ class Main_page( Page ):
|
|||
invites = None,
|
||||
invite_id = None,
|
||||
after_login = None,
|
||||
signup_plan = None,
|
||||
):
|
||||
startup_note_ids = [ startup_note.object_id for startup_note in startup_notes ]
|
||||
|
||||
|
@ -108,6 +109,7 @@ class Main_page( Page ):
|
|||
Input( type = u"hidden", name = u"invites", id = u"invites", value = json( invites ) ),
|
||||
Input( type = u"hidden", name = u"invite_id", id = u"invite_id", value = invite_id ),
|
||||
Input( type = u"hidden", name = u"after_login", id = u"after_login", value = after_login ),
|
||||
Input( type = u"hidden", name = u"signup_plan", id = u"signup_plan", value = signup_plan ),
|
||||
Div(
|
||||
id = u"status_area",
|
||||
),
|
||||
|
|
|
@ -98,8 +98,8 @@ class Upgrade_page( Product_page ):
|
|||
alt = u"More room to stretch out",
|
||||
),
|
||||
Ul(
|
||||
Li( u"More room for your wiki notes." ),
|
||||
Li( u"More room for your documents and files." ),
|
||||
Li( u"More space for your wiki notes." ),
|
||||
Li( u"More space for your documents and files." ),
|
||||
class_ = u"upgrade_text",
|
||||
),
|
||||
Img(
|
||||
|
@ -176,7 +176,8 @@ class Upgrade_page( Product_page ):
|
|||
class_ = u"price_text",
|
||||
separator = u"",
|
||||
),
|
||||
user and user.username and user.rate_plan != index and plan.get( u"button" ).strip() and plan.get( u"button" ) % user.object_id,
|
||||
user and user.username not in ( u"anonymous", None ) and user.rate_plan != index \
|
||||
and plan.get( u"button" ).strip() and plan.get( u"button" ) % user.object_id or None,
|
||||
) or None,
|
||||
( not user or user.username in ( u"anonymous", None ) ) and Div(
|
||||
A(
|
||||
|
|
Reference in New Issue
Block a user