Made upgrade page dynamically generated on the server instead of static html.
This allows things like only displaying subscription buttons if you're logged in.
This commit is contained in:
parent
08cd7057f3
commit
8f00cceb94
2
NEWS
2
NEWS
|
@ -3,6 +3,8 @@
|
|||
* Feature to preview a notebook as a viewer would see it.
|
||||
* Note revisions list now include username of the user who made that
|
||||
revision.
|
||||
* If you go to luminotes.com when you're logged in, you'll be automatically
|
||||
redirected to your first notebook.
|
||||
* Fixed bug where passwords with special characters broke password hashing.
|
||||
* Fixed bug that prevented you from opening a note with a title that looked
|
||||
like an external URL.
|
||||
|
|
|
@ -28,22 +28,32 @@ settings = {
|
|||
"name": "free",
|
||||
"storage_quota_bytes": 30 * MEGABYTE,
|
||||
"notebook_collaboration": False,
|
||||
"fee": None,
|
||||
},
|
||||
{
|
||||
"name": "basic",
|
||||
"storage_quota_bytes": 250 * MEGABYTE,
|
||||
"notebook_collaboration": True,
|
||||
"fee": 5,
|
||||
"button":
|
||||
"""
|
||||
""",
|
||||
},
|
||||
{
|
||||
"name": "standard",
|
||||
"storage_quota_bytes": 500 * MEGABYTE,
|
||||
"notebook_collaboration": True,
|
||||
"fee": 9,
|
||||
"button":
|
||||
"""
|
||||
""",
|
||||
},
|
||||
{
|
||||
"name": "premium",
|
||||
"storage_quota_bytes": 2000 * MEGABYTE,
|
||||
"notebook_collaboration": True,
|
||||
},
|
||||
# {
|
||||
# "name": "premium",
|
||||
# "storage_quota_bytes": 2000 * MEGABYTE,
|
||||
# "notebook_collaboration": True,
|
||||
# "fee": 19,
|
||||
# },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import cherrypy
|
|||
|
||||
from Expose import expose
|
||||
from Expire import strongly_expire
|
||||
from Validate import validate, Valid_int
|
||||
from Validate import validate, Valid_int, Valid_string
|
||||
from Notebooks import Notebooks
|
||||
from Users import Users, grab_user_id
|
||||
from Database import Valid_id
|
||||
|
@ -11,6 +11,7 @@ from model.Notebook import Notebook
|
|||
from model.User import User
|
||||
from view.Main_page import Main_page
|
||||
from view.Notebook_rss import Notebook_rss
|
||||
from view.Upgrade_note import Upgrade_note
|
||||
from view.Json import Json
|
||||
from view.Error_page import Error_page
|
||||
from view.Not_found_page import Not_found_page
|
||||
|
@ -47,9 +48,10 @@ class Root( object ):
|
|||
@validate(
|
||||
note_title = unicode,
|
||||
invite_id = Valid_id( none_okay = True ),
|
||||
after_login = Valid_string( min = 0, max = 100 ),
|
||||
user_id = Valid_id( none_okay = True ),
|
||||
)
|
||||
def default( self, note_title, invite_id = None, user_id = None ):
|
||||
def default( self, note_title, invite_id = None, after_login = None, user_id = None ):
|
||||
"""
|
||||
Convenience method for accessing a note in the main notebook by name rather than by note id.
|
||||
|
||||
|
@ -57,6 +59,8 @@ class Root( object ):
|
|||
@param note_title: title of the note to return
|
||||
@type invite_id: unicode
|
||||
@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 "/")
|
||||
@rtype: unicode
|
||||
@return: rendered HTML page
|
||||
"""
|
||||
|
@ -85,6 +89,8 @@ class Root( object ):
|
|||
result.update( self.__notebooks.contents( main_notebook.object_id, user_id = user_id, note_id = note.object_id ) )
|
||||
if invite_id:
|
||||
result[ "invite_id" ] = invite_id
|
||||
if after_login and after_login.startswith( u"/" ):
|
||||
result[ "after_login" ] = after_login
|
||||
|
||||
return result
|
||||
|
||||
|
@ -144,7 +150,7 @@ class Root( object ):
|
|||
if user:
|
||||
first_notebook = self.__database.select_one( Notebook, user.sql_load_notebooks( parents_only = True, undeleted_only = True ) )
|
||||
if first_notebook:
|
||||
return dict( redirect = "%s/notebooks/%s" % ( https_url, first_notebook.object_id ) )
|
||||
return dict( redirect = u"%s/notebooks/%s" % ( https_url, first_notebook.object_id ) )
|
||||
|
||||
# if the user is logged in and not using https, then redirect to the https version of the page (if available)
|
||||
if https_url and cherrypy.request.remote_addr != https_proxy_ip:
|
||||
|
@ -231,6 +237,40 @@ class Root( object ):
|
|||
|
||||
return result
|
||||
|
||||
@expose( view = Main_page )
|
||||
@grab_user_id
|
||||
@validate(
|
||||
user_id = Valid_id( none_okay = True ),
|
||||
)
|
||||
def upgrade( self, user_id = None ):
|
||||
"""
|
||||
Provide the information necessary to display the Luminotes upgrade page.
|
||||
"""
|
||||
anonymous = self.__database.select_one( User, User.sql_load_by_username( u"anonymous" ) )
|
||||
if anonymous:
|
||||
main_notebook = self.__database.select_one( Notebook, anonymous.sql_load_notebooks( undeleted_only = True ) )
|
||||
else:
|
||||
main_notebook = None
|
||||
|
||||
https_url = self.__settings[ u"global" ].get( u"luminotes.https_url" )
|
||||
result = self.__users.current( user_id )
|
||||
result[ "notebook" ] = main_notebook
|
||||
result[ "startup_notes" ] = self.__database.select_many( Note, main_notebook.sql_load_startup_notes() )
|
||||
result[ "total_notes_count" ] = self.__database.select_one( Note, main_notebook.sql_count_notes() )
|
||||
result[ "note_read_write" ] = False
|
||||
result[ "notes" ] = [ Note.create(
|
||||
object_id = u"upgrade",
|
||||
contents = unicode( Upgrade_note(
|
||||
self.__settings[ u"global" ].get( u"luminotes.rate_plans", [] ),
|
||||
https_url,
|
||||
user_id,
|
||||
) ),
|
||||
notebook_id = main_notebook.object_id,
|
||||
) ]
|
||||
result[ "invites" ] = []
|
||||
|
||||
return result
|
||||
|
||||
# TODO: move this method to controller.Notebooks, and maybe give it a more sensible name
|
||||
@expose( view = Json )
|
||||
def next_id( self ):
|
||||
|
|
|
@ -331,8 +331,9 @@ class Users( object ):
|
|||
password = Valid_string( min = 1, max = 30 ),
|
||||
login_button = unicode,
|
||||
invite_id = Valid_id( none_okay = True ),
|
||||
after_login = Valid_string( min = 0, max = 100 ),
|
||||
)
|
||||
def login( self, username, password, login_button, invite_id = None ):
|
||||
def login( self, username, password, login_button, invite_id = None, after_login = None ):
|
||||
"""
|
||||
Attempt to authenticate the user. If successful, associate the given user with the current
|
||||
session.
|
||||
|
@ -343,6 +344,8 @@ class Users( object ):
|
|||
@param password: the user's password
|
||||
@type invite_id: unicode
|
||||
@param invite_id: id of invite to redeem upon login (optional)
|
||||
@type after_login: unicode
|
||||
@param after_login: URL to redirect to after login (optional, must start with "/")
|
||||
@rtype: json dict
|
||||
@return: { 'redirect': url, 'authenticated': userdict }
|
||||
@raise Authentication_error: invalid username or password
|
||||
|
@ -363,6 +366,9 @@ class Users( object ):
|
|||
|
||||
self.convert_invite_to_access( invite, user.object_id )
|
||||
redirect = u"/notebooks/%s" % invite.notebook_id
|
||||
# if there's an after_login URL, redirect to it
|
||||
elif after_login and after_login.startswith( "/" ):
|
||||
redirect = after_login
|
||||
# otherwise, just redirect to the user's first notebook (if any)
|
||||
elif first_notebook:
|
||||
redirect = u"/notebooks/%s" % first_notebook.object_id
|
||||
|
|
|
@ -314,10 +314,16 @@ class Test_controller( object ):
|
|||
{
|
||||
u"name": u"super",
|
||||
u"storage_quota_bytes": 1337,
|
||||
u"notebook_collaboration": True,
|
||||
u"fee": 1.99,
|
||||
u"button": u"[subscribe here user %s!] button",
|
||||
},
|
||||
{
|
||||
u"name": "extra super",
|
||||
u"storage_quota_bytes": 31337,
|
||||
u"notebook_collaboration": True,
|
||||
u"fee": 199.99,
|
||||
u"button": u"[or here user %s!] button",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -117,6 +117,36 @@ class Test_root( Test_controller ):
|
|||
assert result[ u"invite_id" ] == u"whee"
|
||||
assert result[ u"user" ].object_id == self.anonymous.object_id
|
||||
|
||||
def test_default_with_after_login( self ):
|
||||
after_login = "/foo/bar"
|
||||
|
||||
result = self.http_get(
|
||||
"/my_note?after_login=%s" % after_login,
|
||||
)
|
||||
|
||||
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"after_login" ] == after_login
|
||||
assert result[ u"user" ].object_id == self.anonymous.object_id
|
||||
|
||||
def test_default_with_after_login_with_full_url( self ):
|
||||
after_login = "http://example.com/foo/bar"
|
||||
|
||||
result = self.http_get(
|
||||
"/my_note?after_login=%s" % after_login,
|
||||
)
|
||||
|
||||
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.get( u"after_login" ) is None
|
||||
assert result[ u"user" ].object_id == self.anonymous.object_id
|
||||
|
||||
def test_default_after_login( self ):
|
||||
self.login()
|
||||
|
||||
|
@ -205,6 +235,72 @@ class Test_root( Test_controller ):
|
|||
assert u"error" not in result
|
||||
assert result[ u"notebook" ].object_id == self.privacy_notebook.object_id
|
||||
|
||||
def test_upgrade( self ):
|
||||
result = self.http_get( "/upgrade" )
|
||||
|
||||
assert result[ u"user" ].username == u"anonymous"
|
||||
assert len( result[ u"notebooks" ] ) == 4
|
||||
assert result[ u"notebooks" ][ 0 ].object_id == self.anon_notebook.object_id
|
||||
assert result[ u"notebooks" ][ 0 ].name == self.anon_notebook.name
|
||||
assert result[ u"notebooks" ][ 0 ].read_write == False
|
||||
assert result[ u"notebooks" ][ 0 ].owner == False
|
||||
|
||||
rate_plan = result[ u"rate_plan" ]
|
||||
assert rate_plan
|
||||
assert rate_plan[ u"name" ] == u"super"
|
||||
assert rate_plan[ u"storage_quota_bytes" ] == 1337
|
||||
|
||||
assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
|
||||
assert len( result[ u"startup_notes" ] ) == 0
|
||||
assert result[ u"note_read_write" ] is False
|
||||
|
||||
assert result[ u"notes" ]
|
||||
assert len( result[ u"notes" ] ) == 1
|
||||
assert result[ u"notes" ][ 0 ].title == u"upgrade your wiki"
|
||||
assert result[ u"notes" ][ 0 ].notebook_id == self.anon_notebook.object_id
|
||||
|
||||
contents = result[ u"notes" ][ 0 ].contents
|
||||
assert u"upgrade" in contents
|
||||
assert u"Super" in contents
|
||||
assert u"Extra super" in contents
|
||||
|
||||
# since the user is not logged in, no subscription buttons should be shown
|
||||
assert u"button" not in contents
|
||||
|
||||
def test_upgrade_after_login( self ):
|
||||
self.login()
|
||||
|
||||
result = self.http_get( "/upgrade", session_id = self.session_id )
|
||||
|
||||
assert result[ u"user" ].username == self.username
|
||||
assert len( result[ u"notebooks" ] ) == 5
|
||||
assert result[ u"notebooks" ][ 0 ].object_id == self.notebook.object_id
|
||||
assert result[ u"notebooks" ][ 0 ].name == self.notebook.name
|
||||
assert result[ u"notebooks" ][ 0 ].read_write == False
|
||||
assert result[ u"notebooks" ][ 0 ].owner == False
|
||||
|
||||
rate_plan = result[ u"rate_plan" ]
|
||||
assert rate_plan
|
||||
assert rate_plan[ u"name" ] == u"super"
|
||||
assert rate_plan[ u"storage_quota_bytes" ] == 1337
|
||||
|
||||
assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
|
||||
assert len( result[ u"startup_notes" ] ) == 0
|
||||
assert result[ u"note_read_write" ] is False
|
||||
|
||||
assert result[ u"notes" ]
|
||||
assert len( result[ u"notes" ] ) == 1
|
||||
assert result[ u"notes" ][ 0 ].title == u"upgrade your wiki"
|
||||
assert result[ u"notes" ][ 0 ].notebook_id == self.anon_notebook.object_id
|
||||
|
||||
contents = result[ u"notes" ][ 0 ].contents
|
||||
assert u"upgrade" in contents
|
||||
assert u"Super" in contents
|
||||
assert u"Extra super" in contents
|
||||
|
||||
# since the user is logged in, subscription buttons should be shown
|
||||
assert u"button" in contents
|
||||
|
||||
def test_next_id( self ):
|
||||
result = self.http_get( "/next_id" )
|
||||
|
||||
|
|
|
@ -457,6 +457,30 @@ class Test_users( Test_controller ):
|
|||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
|
||||
def test_current_after_login_with_after_login( self ):
|
||||
after_login = u"/foo/bar"
|
||||
|
||||
result = self.http_post( "/users/login", dict(
|
||||
username = self.username2,
|
||||
password = self.password2,
|
||||
after_login = after_login,
|
||||
login_button = u"login",
|
||||
) )
|
||||
|
||||
assert result[ u"redirect" ] == after_login
|
||||
|
||||
def test_current_after_login_with_after_login_with_full_url( self ):
|
||||
after_login = u"http://this_url/does/not/start/with/a/slash"
|
||||
|
||||
result = self.http_post( "/users/login", dict(
|
||||
username = self.username2,
|
||||
password = self.password2,
|
||||
after_login = after_login,
|
||||
login_button = u"login",
|
||||
) )
|
||||
|
||||
assert result[ u"redirect" ] == u"/"
|
||||
|
||||
def test_update_storage( self ):
|
||||
previous_revision = self.user.revision
|
||||
|
||||
|
|
|
@ -138,9 +138,30 @@ ol li {
|
|||
padding-right: 1em;
|
||||
}
|
||||
|
||||
#upgrade_table_area {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#upgrade_login_text {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#upgrade_table {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #999999;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#upgrade_table th {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#upgrade_table td {
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#upgrade_table .plan_name {
|
||||
|
@ -155,19 +176,41 @@ ol li {
|
|||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
#upgrade_table .price_text {
|
||||
#upgrade_table_small {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #999999;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#upgrade_table_small th {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#upgrade_table_small td {
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#upgrade_table_small .plan_name {
|
||||
width: 33%;
|
||||
text-align: center;
|
||||
background-color: #d0e0f0;
|
||||
}
|
||||
|
||||
.price_text {
|
||||
color: #ff6600;
|
||||
}
|
||||
|
||||
#upgrade_table .month_text {
|
||||
.month_text {
|
||||
padding-top: 0.5em;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
#upgrade_table td {
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
padding: 0.5em;
|
||||
.subscribe_form {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.thumbnail_left {
|
||||
|
|
|
@ -16,10 +16,11 @@ whenever you want.</p>
|
|||
|
||||
<b>Does this cost me anything?</b><br />
|
||||
|
||||
<p>Nope, use of your personal Luminotes wiki is completely free. Soon you will
|
||||
also be able to <a href="/notebooks/%s?note_id=new">upgrade</a> your Luminotes
|
||||
account to get notebook sharing features and additional storage space. But the
|
||||
features you're using now will always remain free.</p>
|
||||
<p>Use of your personal Luminotes wiki is completely free. You also have the
|
||||
option of <a href="/upgrade" target="_top">upgrading</a> your Luminotes
|
||||
account to get notebook sharing features and additional storage space for a
|
||||
reasonable subscription fee. But the features you're using now will always
|
||||
remain free.</p>
|
||||
|
||||
<b>What does Luminotes run on?</b><br />
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
<a href="/notebooks/%s?note_id=new">faq</a> -
|
||||
<a href="/blog" target="_top">blog</a> -
|
||||
<a href="/guide" target="_top">user guide</a> -
|
||||
<a href="/upgrade" target="_top">pricing</a> -
|
||||
<a href="/notebooks/%s?note_id=new">meet the team</a> -
|
||||
<a href="/notebooks/%s?note_id=new">contact info</a> -
|
||||
<a href="/privacy" target="_top">privacy policy</a>
|
||||
<a href="/privacy" target="_top">privacy</a>
|
||||
</div>
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
<h3>upgrade</h3>
|
||||
|
||||
<p>
|
||||
In a few short weeks, you'll be able to upgrade your Luminotes account to get
|
||||
notebook sharing features and additional storage space. Here are some of the
|
||||
features you can look forward to.
|
||||
</p>
|
||||
|
||||
<h3>share your notebook</h3>
|
||||
|
||||
<p>
|
||||
<a href="/static/images/share.png" target="_new"><img
|
||||
src="/static/images/share_thumb.png" class="thumbnail_right" width="200" height="200" /></a>
|
||||
Most of the time, you want to keep your personal wiki all to yourself. But
|
||||
sometimes you simply need to share your work with friends and colleagues. When
|
||||
you upgrade your Luminotes account, you'll be able to invite specific people
|
||||
to collaborate on your wiki simply by entering their email addresses. You can
|
||||
even give them full editing capbilities, so several people can contribute to
|
||||
your wiki notebook.
|
||||
</p>
|
||||
|
||||
<h3>access control</h3>
|
||||
|
||||
<p>
|
||||
<a href="/static/images/access.png" target="_new"><img
|
||||
src="/static/images/access_thumb.png" class="thumbnail_left" width="200" height="200" /></a>
|
||||
With an upgraded Luminotes wiki, you'll decide exactly how much access to give
|
||||
people. Collaborators can make changes to your notebook, while viewers can
|
||||
only read your wiki. And owners have the same complete access to your notebook
|
||||
that you do. When you're done collaborating, a single click revokes a user's
|
||||
notebook access.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Your wiki access control works on a per-notebook basis, so you can easily
|
||||
share one notebook with your friends while keeping your other notebooks
|
||||
completely private.
|
||||
</p>
|
||||
|
||||
<h3>additional storage space</h3>
|
||||
|
||||
<p>
|
||||
An upgraded Luminotes account gets you more than just notebook sharing
|
||||
features. You'll also be treated to way more room for your personal wiki. That
|
||||
means you'll have more space for your notes and ideas, and you won't have to
|
||||
worry about running out of room anytime soon.
|
||||
</p>
|
||||
|
||||
<h3>stay tuned</h3>
|
||||
|
||||
More information about upgrading your Luminotes account will be added as it
|
||||
becomes available. Please consider subscribing to the <a href="/blog"
|
||||
target="_top">Luminotes blog</a> for updates!
|
|
@ -13,7 +13,8 @@ function Wiki( invoker ) {
|
|||
this.rate_plan = evalJSON( getElement( "rate_plan" ).value );
|
||||
this.storage_usage_high = false;
|
||||
this.invites = evalJSON( getElement( "invites" ).value );
|
||||
this.invite_id = getElement( "invite_id" ).value ;
|
||||
this.invite_id = getElement( "invite_id" ).value;
|
||||
this.after_login = getElement( "after_login" ).value;
|
||||
|
||||
var total_notes_count_node = getElement( "total_notes_count" );
|
||||
if ( total_notes_count_node )
|
||||
|
@ -652,8 +653,11 @@ 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" || url == "/users/login" ) {
|
||||
args[ "invite_id" ] = self.invite_id;
|
||||
if ( url == "/users/login" )
|
||||
args[ "after_login" ] = self.after_login;
|
||||
}
|
||||
|
||||
self.invoker.invoke( url, "POST", args, callback, form );
|
||||
} );
|
||||
|
|
|
@ -24,7 +24,6 @@ class Initializer( object ):
|
|||
( u"password reset.html", False ),
|
||||
( u"advanced browser features.html", False ),
|
||||
( u"supported browsers.html", False ),
|
||||
( u"upgrade.html", False ),
|
||||
]
|
||||
|
||||
def __init__( self, database, nuke = False ):
|
||||
|
|
|
@ -24,7 +24,6 @@ class Updater( object ):
|
|||
( u"password reset.html", False ),
|
||||
( u"advanced browser features.html", False ),
|
||||
( u"supported browsers.html", False ),
|
||||
( u"upgrade.html", False ),
|
||||
]
|
||||
|
||||
def __init__( self, database, navigation_note_id = None ):
|
||||
|
|
|
@ -31,6 +31,7 @@ class Main_page( Page ):
|
|||
deleted_id = None,
|
||||
invites = None,
|
||||
invite_id = None,
|
||||
after_login = None,
|
||||
):
|
||||
startup_note_ids = [ startup_note.object_id for startup_note in startup_notes ]
|
||||
|
||||
|
@ -102,6 +103,7 @@ class Main_page( Page ):
|
|||
Input( type = u"hidden", name = u"deleted_id", id = u"deleted_id", value = deleted_id ),
|
||||
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 ),
|
||||
Div(
|
||||
id = u"status_area",
|
||||
),
|
||||
|
|
|
@ -25,6 +25,7 @@ class Notebook_rss( Rss_channel ):
|
|||
deleted_id = None,
|
||||
invites = None,
|
||||
invite_id = None,
|
||||
after_login = None,
|
||||
):
|
||||
if notebook.name == u"Luminotes":
|
||||
notebook_path = u"/"
|
||||
|
|
|
@ -3,8 +3,6 @@ from Tags import Span, H3, P, A
|
|||
|
||||
class Redeem_invite_note( Span ):
|
||||
def __init__( self, invite, notebook ):
|
||||
title = None
|
||||
|
||||
Span.__init__(
|
||||
self,
|
||||
H3( notebook.name ),
|
||||
|
|
|
@ -3,8 +3,6 @@ from Tags import Span, H3, P, Form, P, Div, Strong, Br, Input
|
|||
|
||||
class Redeem_reset_note( Span ):
|
||||
def __init__( self, password_reset_id, users ):
|
||||
title = None
|
||||
|
||||
Span.__init__(
|
||||
self,
|
||||
H3( u"complete your password reset" ),
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
from Tags import Div, Span, H3, P, A, Table, Tr, Th, Td, Br, Img
|
||||
|
||||
|
||||
class Upgrade_note( Span ):
|
||||
def __init__( self, rate_plans, https_url, user_id ):
|
||||
MEGABYTE = 1024 * 1024
|
||||
|
||||
Span.__init__(
|
||||
self,
|
||||
H3( u"upgrade your wiki" ),
|
||||
P(
|
||||
u"When you",
|
||||
A( u"sign up", href = https_url + u"/sign_up", target = u"_top" ),
|
||||
"""
|
||||
for a free Luminotes account, you get a full-featured
|
||||
personal wiki available wherever you go. And if you upgrade your
|
||||
Luminotes account, you'll also get powerful notebook sharing features
|
||||
so that you and your friends can all collaborate on your wiki notebook.
|
||||
""",
|
||||
),
|
||||
P(
|
||||
Table(
|
||||
self.fee_row( rate_plans, user_id ),
|
||||
Tr(
|
||||
Td( u"included storage space", class_ = u"feature_name" ),
|
||||
[ Td(
|
||||
plan[ u"storage_quota_bytes" ] // MEGABYTE, " MB",
|
||||
) for plan in rate_plans ],
|
||||
),
|
||||
Tr(
|
||||
Td( u"unlimited wiki notebooks", class_ = u"feature_name" ),
|
||||
[ Td(
|
||||
Img( src = u"/static/images/check.png", width = u"20", height = u"17" ),
|
||||
) for plan in rate_plans ],
|
||||
),
|
||||
Tr(
|
||||
Td( u"friendly email support", class_ = u"feature_name" ),
|
||||
[ Td(
|
||||
Img( src = u"/static/images/check.png", width = u"20", height = u"17" ),
|
||||
) for plan in rate_plans ],
|
||||
),
|
||||
Tr(
|
||||
Td( u"multi-user collaboration", class_ = u"feature_name" ),
|
||||
[ Td(
|
||||
plan[ u"notebook_collaboration" ] and
|
||||
Img( src = u"/static/images/check.png", width = u"20", height = u"17" ) or u" ",
|
||||
) for plan in rate_plans ],
|
||||
),
|
||||
Tr(
|
||||
Td( u"wiki access control", class_ = u"feature_name" ),
|
||||
[ Td(
|
||||
plan[ u"notebook_collaboration" ] and
|
||||
Img( src = u"/static/images/check.png", width = u"20", height = u"17" ) or u" ",
|
||||
) for plan in rate_plans ],
|
||||
),
|
||||
border = u"1",
|
||||
id = u"upgrade_table",
|
||||
),
|
||||
( not user_id ) and P(
|
||||
u"To upgrade your Luminotes account, please",
|
||||
A( u"login", href = https_url + u"/login?after_login=/upgrade", target = u"_top" ),
|
||||
u"first!",
|
||||
id = u"upgrade_login_text",
|
||||
) or None,
|
||||
id = u"upgrade_table_area",
|
||||
),
|
||||
|
||||
H3( u"share your notebook" ),
|
||||
P(
|
||||
A(
|
||||
Img(
|
||||
src = u"/static/images/share_thumb.png",
|
||||
class_ = u"thumbnail_right",
|
||||
width = u"200",
|
||||
height = u"200",
|
||||
),
|
||||
href = u"/static/images/share.png",
|
||||
target = u"_new",
|
||||
),
|
||||
u"""
|
||||
Most of the time, you want to keep your personal wiki all to yourself. But
|
||||
sometimes you simply need to share your work with friends and colleagues.
|
||||
""",
|
||||
),
|
||||
P(
|
||||
u"""
|
||||
With an upgraded Luminotes account, you'll be able to invite specific people
|
||||
to collaborate on your wiki simply by entering their email addresses. You can
|
||||
even give them full editing capbilities, so several people can contribute to
|
||||
your wiki notebook. And you can invite as many people as you want to
|
||||
collaborate on your wiki. They only need to sign up for a free Luminotes
|
||||
account to particpate.
|
||||
"""
|
||||
),
|
||||
H3( u"wiki access control" ),
|
||||
P(
|
||||
A(
|
||||
Img(
|
||||
src = u"/static/images/access_thumb.png",
|
||||
class_ = u"thumbnail_left",
|
||||
width = u"200",
|
||||
height = u"200",
|
||||
),
|
||||
href = u"/static/images/access.png",
|
||||
target = u"_new",
|
||||
),
|
||||
u"""
|
||||
With an upgraded Luminotes wiki, you'll decide exactly how much access to give
|
||||
people. Collaborators can make changes to your notebook, while viewers can
|
||||
only read your wiki. And owners have the same complete access to your notebook
|
||||
that you do. When you're done collaborating, a single click revokes a user's
|
||||
notebook access.
|
||||
""",
|
||||
),
|
||||
P(
|
||||
u"""
|
||||
Your wiki access control works on a per-notebook basis, so you can easily
|
||||
share one notebook with your friends while keeping your other notebooks
|
||||
completely private.
|
||||
""",
|
||||
),
|
||||
H3( u"additional storage space" ),
|
||||
P(
|
||||
u"""
|
||||
An upgraded Luminotes account gets you more than just notebook sharing
|
||||
features. You'll also be treated to way more room for your personal wiki. That
|
||||
means you'll have more space for your notes and ideas, and you won't have to
|
||||
worry about running out of room anytime soon.
|
||||
""",
|
||||
),
|
||||
H3( u"no questions asked money-back guarantee" ),
|
||||
P(
|
||||
u"""
|
||||
If you upgrade your Luminotes account and find that it's not meeting your
|
||||
needs, then simply request a refund within 30 days and your money will be
|
||||
returned in full without any questions.
|
||||
"""
|
||||
),
|
||||
P(
|
||||
u"""
|
||||
And no matter how long you've been using an upgraded Luminotes account, you
|
||||
can cancel online anytime. You won't have to send email or talk to anyone in a
|
||||
call center. If you do cancel, you keep all of your wiki notebooks and simply
|
||||
return to a free account.
|
||||
""",
|
||||
),
|
||||
P(
|
||||
Table(
|
||||
self.fee_row( rate_plans, user_id, include_blank = False ),
|
||||
Tr(
|
||||
[ Td(
|
||||
plan[ u"storage_quota_bytes" ] // MEGABYTE, " MB",
|
||||
) for plan in rate_plans ],
|
||||
),
|
||||
border = u"1",
|
||||
id = u"upgrade_table_small",
|
||||
),
|
||||
( not user_id ) and P(
|
||||
u"Please",
|
||||
A( u"login", href = https_url + u"/login?after_login=/upgrade", target = u"_top" ),
|
||||
u"to upgrade your wiki!",
|
||||
id = u"upgrade_login_text",
|
||||
) or None,
|
||||
id = u"upgrade_table_area",
|
||||
),
|
||||
)
|
||||
|
||||
def fee_row( self, rate_plans, user_id, include_blank = True ):
|
||||
return Tr(
|
||||
include_blank and Th( u" " ) or None,
|
||||
[ Th(
|
||||
plan[ u"name" ].capitalize(),
|
||||
plan[ u"fee" ] and Div(
|
||||
Span(
|
||||
u"$%s" % plan[ u"fee" ],
|
||||
Span( u"/month", class_ = u"month_text" ),
|
||||
class_ = u"price_text",
|
||||
separator = u"",
|
||||
),
|
||||
user_id and plan.get( u"button" ) % user_id or None,
|
||||
) or None,
|
||||
class_ = u"plan_name",
|
||||
) for plan in rate_plans ],
|
||||
)
|
Reference in New Issue