witten
/
luminotes
Archived
1
0
Fork 0

* Started a static upgrade HTML file with rate plans.

* Fixed Invite.sql_update() to have SQL params in proper order.
 * Fixed bug where email addresses containing "-" were considered invalid.
 * Made UI for inviting other people to your notebook.
 * Tweaked the rate plans and added a new one.
This commit is contained in:
Dan Helfman 2007-12-11 01:15:03 +00:00
parent a615f65d29
commit 5554b1df17
13 changed files with 438 additions and 102 deletions

View File

@ -25,16 +25,24 @@ settings = {
"luminotes.support_email": "",
"luminotes.rate_plans": [
{
"name": "basic",
"name": "free",
"storage_quota_bytes": 30 * MEGABYTE,
"notebook_sharing": False,
},
{
"name": "basic",
"storage_quota_bytes": 250 * MEGABYTE,
"notebook_sharing": True,
},
{
"name": "standard",
"storage_quota_bytes": 100 * MEGABYTE,
"storage_quota_bytes": 500 * MEGABYTE,
"notebook_sharing": True,
},
{
"name": "professional",
"storage_quota_bytes": 300 * MEGABYTE,
"name": "premium",
"storage_quota_bytes": 1000 * MEGABYTE,
"notebook_sharing": True,
},
],
},

View File

@ -17,8 +17,8 @@ from view.Redeem_reset_note import Redeem_reset_note
USERNAME_PATTERN = re.compile( "^[a-zA-Z0-9]+$" )
EMAIL_ADDRESS_PATTERN = re.compile( "^[\w.+]+@\w+(\.\w+)+$" )
EMBEDDED_EMAIL_ADDRESS_PATTERN = re.compile( "(?:^|[\s,<])([\w.+]+@\w+(?:\.\w+)+)(?:[\s,>]|$)" )
EMAIL_ADDRESS_PATTERN = re.compile( "^[\w.%+-]+@[\w-]+(\.[\w-]+)+$" )
EMBEDDED_EMAIL_ADDRESS_PATTERN = re.compile( "(?:^|[\s,<])([\w.%+-]+@[\w-]+(?:\.[\w-]+)+)(?:[\s,>]|$)" )
WHITESPACE_OR_COMMA_PATTERN = re.compile( "[\s,]" )
@ -508,9 +508,9 @@ class Users( object ):
# create an email message with a unique link
message = Message.Message()
message[ u"from" ] = u"Luminotes support <%s>" % self.__support_email
message[ u"to" ] = email_address
message[ u"subject" ] = u"Luminotes password reset"
message[ u"From" ] = u"Luminotes support <%s>" % self.__support_email
message[ u"To" ] = email_address
message[ u"Subject" ] = u"Luminotes password reset"
message.set_payload(
u"Someone has requested a password reset for a Luminotes user with your email\n" +
u"address. If this someone is you, please visit the following link for a\n" +
@ -523,7 +523,7 @@ class Users( object ):
# send the message out through localhost's smtp server
server = smtplib.SMTP()
server.connect()
server.sendmail( message[ u"from" ], [ email_address ], message.as_string() )
server.sendmail( message[ u"From" ], [ email_address ], message.as_string() )
server.quit()
return dict(
@ -655,12 +655,11 @@ class Users( object ):
@validate(
notebook_id = Valid_id(),
email_addresses = unicode,
read_write = Valid_bool(),
owner = Valid_bool(),
access = Valid_string(),
invite_button = unicode,
user_id = Valid_id( none_okay = True ),
)
def send_invites( self, notebook_id, email_addresses, read_write, owner, invite_button, user_id = None ):
def send_invites( self, notebook_id, email_addresses, access, invite_button, user_id = None ):
"""
Send notebook invitations to the given email addresses.
@ -668,10 +667,8 @@ class Users( object ):
@param notebook_id: id of the notebook that the invitation is for
@type email_addresses: unicode
@param email_addresses: a string containing whitespace- or comma-separated email addresses
@type read_write: bool
@param read_write: whether the invitation is for read-write access
@type owner: bool
@param owner: whether the invitation is for owner-level access
@type access: unicode
@param access: type of access to grant, either "collaborator", "viewer", or "owner"
@type invite_button: unicode
@param invite_button: ignored
@type user_id: unicode
@ -683,10 +680,22 @@ class Users( object ):
@raise Access_error: user_id doesn't have owner-level notebook access to send an invite
"""
if len( email_addresses ) < 5:
raise Invite_error( u"Please enter at least one email valid address." )
raise Invite_error( u"Please enter at least one valid email address." )
if len( email_addresses ) > 5000:
raise Invite_error( u"Please enter fewer email addresses." )
if access == u"collaborator":
read_write = True
owner = False
elif access == u"viewer":
read_write = False
owner = False
elif access == u"owner":
read_write = True
owner = True
else:
raise Access_error()
if not self.check_access( user_id, notebook_id, read_write = True, owner = True ):
raise Access_error()
@ -729,11 +738,11 @@ class Users( object ):
# create an email message with a unique invitation link
notebook_name = notebook.name.strip().replace( "\n", " " ).replace( "\r", " " )
message = Message.Message()
message[ u"from" ] = user.email_address or u"Luminotes personal wiki <%s>" % self.__support_email
if not user.email_address:
message[ u"sender" ] = u"Luminotes personal wiki <%s>" % self.__support_email
message[ u"to" ] = email_address
message[ u"subject" ] = notebook_name
message[ u"From" ] = user.email_address or u"Luminotes personal wiki <%s>" % self.__support_email
if user.email_address:
message[ u"Sender" ] = u"Luminotes personal wiki <%s>" % self.__support_email
message[ u"To" ] = email_address
message[ u"Subject" ] = notebook_name
message.set_payload(
u"I've shared a wiki with you called \"%s\"\n" % notebook_name +
u"Please visit the following link to view it online:\n\n" +
@ -743,7 +752,7 @@ class Users( object ):
# send the message out through localhost's smtp server
server = smtplib.SMTP()
server.connect()
server.sendmail( message[ u"from" ], [ email_address ], message.as_string() )
server.sendmail( message[ u"From" ], [ email_address ], message.as_string() )
server.quit()
self.__database.commit()

View File

@ -23,13 +23,13 @@ class Test_users( Test_controller ):
self.username = u"mulder"
self.password = u"trustno1"
self.email_address = u"outthere@example.com"
self.email_address = u"out-there@example.com"
self.new_username = u"reynolds"
self.new_password = u"shiny"
self.new_email_address = u"capn@example.com"
self.username2 = u"scully"
self.password2 = u"trustsome1"
self.email_address2 = u"outthere@example.com"
self.email_address2 = u"out-there@example.com"
self.user = None
self.user2 = None
self.anonymous = None
@ -769,8 +769,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -783,7 +782,100 @@ class Test_users( Test_controller ):
assert self.email_address in from_address
assert to_addresses == email_addresses_list
assert self.notebooks[ 0 ].name in message
assert self.INVITE_LINK_PATTERN.search( message )
matches = self.INVITE_LINK_PATTERN.search( message )
invite_id = matches.group( 2 )
assert invite_id
# assert that the invite has the read_write / owner flags set appropriately
invite_list = self.database.objects.get( invite_id )
assert invite_list
assert len( invite_list ) == 1
invite = invite_list[ -1 ]
assert invite
assert invite.read_write is False
assert invite.owner is False
def test_send_invites_collaborator( self ):
# trick send_invites() into using a fake SMTP server
Stub_smtp.reset()
smtplib.SMTP = Stub_smtp
self.login()
self.user.rate_plan = 1
self.database.save( self.user )
email_addresses_list = [ u"foo@example.com" ]
email_addresses = email_addresses_list[ 0 ]
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
access = u"collaborator",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
assert u"An invitation has been sent." in result[ u"message" ]
assert smtplib.SMTP.connected == False
assert len( smtplib.SMTP.emails ) == 1
( from_address, to_addresses, message ) = smtplib.SMTP.emails[ 0 ]
assert self.email_address in from_address
assert to_addresses == email_addresses_list
assert self.notebooks[ 0 ].name in message
matches = self.INVITE_LINK_PATTERN.search( message )
invite_id = matches.group( 2 )
assert invite_id
# assert that the invite has the read_write / owner flags set appropriately
invite_list = self.database.objects.get( invite_id )
assert invite_list
assert len( invite_list ) == 1
invite = invite_list[ -1 ]
assert invite
assert invite.read_write is True
assert invite.owner is False
def test_send_invites_owner( self ):
# trick send_invites() into using a fake SMTP server
Stub_smtp.reset()
smtplib.SMTP = Stub_smtp
self.login()
self.user.rate_plan = 1
self.database.save( self.user )
email_addresses_list = [ u"foo@example.com" ]
email_addresses = email_addresses_list[ 0 ]
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
access = u"owner",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
assert u"An invitation has been sent." in result[ u"message" ]
assert smtplib.SMTP.connected == False
assert len( smtplib.SMTP.emails ) == 1
( from_address, to_addresses, message ) = smtplib.SMTP.emails[ 0 ]
assert self.email_address in from_address
assert to_addresses == email_addresses_list
assert self.notebooks[ 0 ].name in message
matches = self.INVITE_LINK_PATTERN.search( message )
invite_id = matches.group( 2 )
assert invite_id
# assert that the invite has the read_write / owner flags set appropriately
invite_list = self.database.objects.get( invite_id )
assert invite_list
assert len( invite_list ) == 1
invite = invite_list[ -1 ]
assert invite
assert invite.read_write is True
assert invite.owner is True
def test_send_invites_multiple( self ):
Stub_smtp.reset()
@ -800,8 +892,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -834,8 +925,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -868,8 +958,7 @@ class Test_users( Test_controller ):
self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
@ -881,8 +970,7 @@ class Test_users( Test_controller ):
self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = True,
owner = True,
access = u"owner",
invite_button = u"send invites",
), session_id = self.session_id )
@ -922,8 +1010,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -951,8 +1038,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -974,8 +1060,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -996,8 +1081,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -1018,8 +1102,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -1041,8 +1124,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -1066,8 +1148,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -1091,8 +1172,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -1111,8 +1191,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = self.notebooks[ 0 ].object_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]
@ -1132,8 +1211,7 @@ class Test_users( Test_controller ):
result = self.http_post( "/users/send_invites", dict(
notebook_id = unknown_notebook_id,
email_addresses = email_addresses,
read_write = False,
owner = False,
access = u"viewer",
invite_button = u"send invites",
), session_id = self.session_id )
session_id = result[ u"session_id" ]

View File

@ -86,9 +86,9 @@ class Invite( Persistent ):
def sql_update( self ):
return "update invite set revision = %s, from_user_id = %s, notebook_id = %s, email_address = %s, read_write = %s, owner = %s, redeemed_user_id = %s where id = %s;" % \
( quote( self.object_id ), quote( self.revision ), quote( self.__from_user_id ), quote( self.__notebook_id ),
( quote( self.revision ), quote( self.__from_user_id ), quote( self.__notebook_id ),
quote( self.__email_address ), quote( self.__read_write and "t" or "f" ), quote( self.__owner and "t" or "f" ),
quote( self.__redeemed_user_id ) )
quote( self.__redeemed_user_id ), quote( self.object_id ) )
def sql_load_similar( self ):
# select unredeemed invitations with the same from_user_id, notebook_id, and email_address as this invitation

View File

@ -79,6 +79,13 @@ h3 {
border: #999999 1px solid;
}
.textarea_field {
margin-top: 0.25em;
padding: 0.25em;
border: #999999 1px solid;
overflow: auto;
}
ul {
list-style-type: disc;
}
@ -99,3 +106,49 @@ ol li {
padding-top: 0.5em;
font-size: 90%;
}
.radio_link {
color: #000000;
text-decoration: none;
}
.radio_link:hover {
color: #ff6600;
text-decoration: none;
}
#access_table td {
padding-right: 1em;
}
#upgrade_table {
border-collapse: collapse;
border: 1px solid #999999;
}
#upgrade_table .plan_name {
width: 16%;
text-align: center;
background-color: #d0e0f0;
}
#upgrade_table .feature_name {
width: 36%;
text-align: left;
background-color: #fafafa;
}
#upgrade_table .price_text {
color: #ff6600;
}
#upgrade_table .month_text {
padding-top: 0.5em;
font-size: 75%;
}
#upgrade_table td {
text-align: center;
background-color: #fafafa;
padding: 0.5em;
}

45
static/html/upgrade.html Normal file
View File

@ -0,0 +1,45 @@
<h3>upgrade</h3>
<table border="1" cellspacing="0" cellpadding="10" id="upgrade_table">
<tr>
<th>&nbsp;</th>
<th class="plan_name">Free</th>
<th class="plan_name">Basic<br /><span class="price_text">$5<span class="month_text">/month</span></span></th>
<th class="plan_name">Standard<br /><span class="price_text">$9<span class="month_text">/month</span></span></th>
<th class="plan_name">Premium<br /><span class="price_text">$14<span class="month_text">/month</span></span></th>
<tr>
<td class="feature_name">included storage space</td>
<td>30 MB</td>
<td>250 MB</td>
<td>500 MB</td>
<td>1000 MB</td>
</tr>
<tr>
<td class="feature_name">unlimited wiki notebooks</td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
</tr>
<tr>
<td class="feature_name">friendly email support</td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
</tr>
<tr>
<td class="feature_name">multi-user collaboration</td>
<td>&nbsp;</td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
</tr>
<tr>
<td class="feature_name">wiki access control</td>
<td>&nbsp;</td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
<td><img src="../../static/images/check.png" width="20" height="17" /></td>
</tr>
</table>

BIN
static/images/check.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

BIN
static/images/check.xcf Normal file

Binary file not shown.

View File

@ -136,48 +136,79 @@ Editor.prototype.finish_init = function () {
if ( this.read_write ) {
connect( this.document, "onkeydown", function ( event ) { self.key_pressed( event ); } );
connect( this.document, "onkeyup", function ( event ) { self.key_released( event ); } );
connect( this.document, "onblur", function ( event ) { self.blurred( event ); } );
connect( this.document, "onfocus", function ( event ) { self.focused( event ); } );
connect( this.document.body, "onblur", function ( event ) { self.blurred( event ); } );
connect( this.document.body, "onfocus", function ( event ) { self.focused( event ); } );
connect( this.iframe.contentWindow, "onblur", function ( event ) { self.blurred( event ); } );
connect( this.iframe.contentWindow, "onfocus", function ( event ) { self.focused( event ); } );
}
connect( this.document, "onblur", function ( event ) { self.blurred( event ); } );
connect( this.document, "onfocus", function ( event ) { self.focused( event ); } );
connect( this.document.body, "onblur", function ( event ) { self.blurred( event ); } );
connect( this.document.body, "onfocus", function ( event ) { self.focused( event ); } );
connect( this.iframe.contentWindow, "onblur", function ( event ) { self.blurred( event ); } );
connect( this.iframe.contentWindow, "onfocus", function ( event ) { self.focused( event ); } );
connect( this.document, "onclick", function ( event ) { self.mouse_clicked( event ); } );
// special case: don't handle mouse click for share_notebook magic note, so radio button links
// work as intended
if ( this.id != "share_notebook" )
connect( this.document, "onclick", function ( event ) { self.mouse_clicked( event ); } );
// special-case: connect any submit buttons within the contents of this note
var signup_button = withDocument( this.document, function () { return getElement( "signup_button" ); } );
if ( signup_button ) {
var signup_form = withDocument( this.document, function () { return getElement( "signup_form" ); } );
connect( signup_button, "onclick", function ( event ) {
signal( self, "submit_form", "/users/signup", signup_form ); event.stop();
} );
}
withDocument( this.document, function () {
var signup_button = getElement( "signup_button" );
if ( signup_button ) {
var signup_form = getElement( "signup_form" );
connect( signup_button, "onclick", function ( event ) {
signal( self, "submit_form", "/users/signup", signup_form ); event.stop();
} );
}
var login_button = withDocument( this.document, function () { return getElement( "login_button" ); } );
if ( login_button ) {
var login_form = withDocument( this.document, function () { return getElement( "login_form" ); } );
connect( login_button, "onclick", function ( event ) {
signal( self, "submit_form", "/users/login", login_form ); event.stop();
} );
}
var login_button = getElement( "login_button" );
if ( login_button ) {
var login_form = getElement( "login_form" );
connect( login_button, "onclick", function ( event ) {
signal( self, "submit_form", "/users/login", login_form ); event.stop();
} );
}
var send_reset_button = withDocument( this.document, function () { return getElement( "send_reset_button" ); } );
if ( send_reset_button ) {
var send_reset_form = withDocument( this.document, function () { return getElement( "send_reset_form" ); } );
connect( send_reset_button, "onclick", function ( event ) {
signal( self, "submit_form", "/users/send_reset", send_reset_form ); event.stop();
} );
}
var send_reset_button = getElement( "send_reset_button" );
if ( send_reset_button ) {
var send_reset_form = getElement( "send_reset_form" );
connect( send_reset_button, "onclick", function ( event ) {
signal( self, "submit_form", "/users/send_reset", send_reset_form ); event.stop();
} );
}
var reset_button = withDocument( this.document, function () { return getElement( "reset_button" ); } );
if ( reset_button ) {
var reset_form = withDocument( this.document, function () { return getElement( "reset_form" ); } );
connect( reset_button, "onclick", function ( event ) {
signal( self, "submit_form", "/users/reset_password", reset_form ); event.stop();
} );
}
var reset_button = getElement( "reset_button" );
if ( reset_button ) {
var reset_form = getElement( "reset_form" );
connect( reset_button, "onclick", function ( event ) {
signal( self, "submit_form", "/users/reset_password", reset_form ); event.stop();
} );
}
var invite_button = getElement( "invite_button" );
if ( invite_button ) {
var collaborators_radio = getElement( "collaborators_radio" );
connect( "collaborators_link", "onclick", function ( event ) {
collaborators_radio.checked = true;
event.stop();
} );
var viewers_radio = getElement( "viewers_radio" );
connect( "viewers_link", "onclick", function ( event ) {
viewers_radio.checked = true;
event.stop();
} );
var owners_radio = getElement( "owners_radio" );
connect( "owners_link", "onclick", function ( event ) {
owners_radio.checked = true;
event.stop();
} );
var invite_form = getElement( "invite_form" );
connect( invite_button, "onclick", function ( event ) {
signal( self, "submit_form", "/users/send_invites", invite_form ); event.stop();
} );
}
} );
// browsers such as Firefox, but not Opera
if ( this.iframe.contentDocument && !/Opera/.test( navigator.userAgent ) && this.read_write )

View File

@ -146,8 +146,8 @@ Wiki.prototype.display_storage_usage = function( storage_bytes ) {
Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_write, skip_empty_message ) {
var self = this;
// if this is the trash, display a list of all deleted notebooks
if ( this.notebook.name == "trash" ) {
// if this is the trash and the user has owner-level access, then display a list of all deleted notebooks
if ( this.notebook.owner && this.notebook.name == "trash" ) {
var heading_shown = false;
var deleted_notebooks = getElement( "deleted_notebooks" );
@ -312,6 +312,14 @@ Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_wri
event.stop();
} );
}
var share_notebook_link = getElement( "share_notebook_link" );
if ( share_notebook_link ) {
connect( share_notebook_link, "onclick", function ( event ) {
self.load_editor( "share this notebook", "null", null, null, getElement( "notes_top" ) );
event.stop();
} );
}
}
Wiki.prototype.background_clicked = function ( event ) {
@ -413,6 +421,16 @@ Wiki.prototype.load_editor = function ( note_title, note_id, revision, link, pos
this.display_search_results();
return;
}
if ( note_title == "share this notebook" ) {
var editor = this.open_editors[ note_title ];
if ( editor ) {
editor.highlight();
return;
}
this.share_notebook();
return;
}
// but if the note corresponding to the link's title is already open, highlight it and bail
if ( !revision ) {
@ -457,7 +475,7 @@ Wiki.prototype.resolve_link = function ( note_title, link, callback ) {
if ( link && link.target )
link.removeAttribute( "target" );
if ( note_title == "all notes" || note_title == "search results" ) {
if ( note_title == "all notes" || note_title == "search results" || note_title == "share this notebook" ) {
link.href = "/notebooks/" + this.notebook_id + "?" + queryString(
[ "title", "note_id" ],
[ note_title, "null" ]
@ -465,8 +483,10 @@ Wiki.prototype.resolve_link = function ( note_title, link, callback ) {
if ( callback ) {
if ( note_title == "all notes" )
callback( "list of all notes in this notebook" );
else
if ( note_title == "search results" )
callback( "current search results" );
else
callback( "share this notebook with others" );
}
return;
}
@ -1220,6 +1240,80 @@ Wiki.prototype.display_all_notes_list = function ( result ) {
this.all_notes_editor = this.create_editor( "all_notes", "<h3>all notes</h3>" + list_holder.innerHTML, undefined, undefined, undefined, false, true, true, getElement( "notes_top" ) );
}
Wiki.prototype.share_notebook = function () {
this.clear_messages();
this.clear_pulldowns();
var share_notebook_frame = getElement( "note_share_notebook" );
if ( share_notebook_frame ) {
share_notebook_frame.editor.highlight();
return;
}
if ( !this.rate_plan.notebook_sharing ) {
this.display_message(
"If you'd like to share your notebook, please ",
[ createDOM( "a", { "href": "/upgrade", "target": "_new" }, "upgrade" ),
" your account first." ]
);
return;
}
var collaborators_link = createDOM( "a",
{ "href": "#", "id": "collaborators_link", "class": "radio_link", "title": "Collaborators may view and edit this notebook." },
"collaborators"
);
var viewers_link = createDOM( "a",
{ "href": "#", "id": "viewers_link", "class": "radio_link", "title": "Viewers may only view this notebook." },
"viewers"
);
var owners_link = createDOM( "a",
{ "href": "#", "id": "owners_link", "class": "radio_link", "title": "Owners may view, edit, rename, delete, and invite people to this notebook." },
"owners"
);
var collaborators_radio = createDOM( "input",
{ "type": "radio", "id": "collaborators_radio", "name": "access", "value": "collaborator", "checked": "true" }
);
var viewers_radio = createDOM( "input",
{ "type": "radio", "id": "viewers_radio", "name": "access", "value": "viewer" }
);
var owners_radio = createDOM( "input",
{ "type": "radio", "id": "owners_radio", "name": "access", "value": "owner" }
)
var div = createDOM( "div", {},
createDOM( "form", { "id": "invite_form" },
createDOM( "input", { "type": "hidden", "name": "notebook_id", "value": this.notebook_id } ),
createDOM( "p", {},
createDOM( "b", {}, "people to invite" ),
createDOM( "br", {} ),
createDOM( "textarea",
{ "name": "email_addresses", "class": "textarea_field", "cols": "40", "rows": "4", "wrap": "off" }
)
),
createDOM( "p", {}, "Please separate email addresses with commas, spaces, or the enter key." ),
createDOM( "p", {},
createDOM( "p", {}, "Invite these people as:" ),
createDOM( "table" , { "id": "access_table" },
createDOM( "tr", {},
createDOM( "td", {}, collaborators_radio, collaborators_link ),
createDOM( "td", {}, viewers_radio, viewers_link ),
createDOM( "td", {}, owners_radio, owners_link )
)
)
),
createDOM( "p", {},
createDOM( "input",
{ "type": "submit", "name": "invite_button", "id": "invite_button", "class": "button", "value": "send invites" }
)
)
)
);
this.create_editor( "share_notebook", "<h3>share this notebook</h3>" + div.innerHTML, undefined, undefined, undefined, false, true, true, getElement( "notes_top" ) );
}
Wiki.prototype.display_message = function ( text, nodes, position_after ) {
this.clear_messages();
this.clear_pulldowns();
@ -1432,7 +1526,7 @@ Wiki.prototype.remove_all_notes_link = function ( note_id ) {
Wiki.prototype.add_all_notes_link = function ( note_id, note_title ) {
if ( !this.all_notes_editor ) return;
if ( note_title == "all notes" || note_title == "search results" ) return;
if ( note_title == "all notes" || note_title == "search results" || note_title == "share this notebook" ) return;
if ( !note_title || note_title.length == 0 )
note_title = "untitled note";
@ -1709,7 +1803,7 @@ function Options_pulldown( wiki, notebook_id, invoker, editor ) {
this.invoker = invoker;
this.editor = editor;
this.startup_checkbox = createDOM( "input", { "type": "checkbox", "class": "pulldown_checkbox" } );
this.startup_toggle = createDOM( "a", { "href": "", "class": "pulldown_link", "title": "Display this note whenever the notebook is loaded." },
this.startup_toggle = createDOM( "a", { "href": "#", "class": "pulldown_link", "title": "Display this note whenever the notebook is loaded." },
"show on startup"
);
@ -1840,6 +1934,12 @@ function Link_pulldown( wiki, notebook_id, invoker, editor, link ) {
return;
}
if ( title == "share this notebook" ) {
this.title_field.value = title;
this.display_summary( title, "share this notebook with others" );
return;
}
this.invoker.invoke(
"/notebooks/load_note_by_title", "GET", {
"notebook_id": this.notebook_id,

View File

@ -24,6 +24,7 @@ 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 ):

View File

@ -24,6 +24,7 @@ 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 ):

View File

@ -45,7 +45,7 @@ class Link_area( Div ):
) or None,
notebook.read_write and Span(
( notebook.name != u"trash" ) and Div(
( notebook.owner and notebook.name != u"trash" ) and Div(
A(
u"rename notebook",
href = u"#",
@ -55,7 +55,7 @@ class Link_area( Div ):
class_ = u"link_area_item",
) or None,
( notebook.name != u"trash" ) and Div(
( notebook.owner and notebook.name != u"trash" ) and Div(
A(
u"delete notebook",
href = u"#",
@ -75,6 +75,16 @@ class Link_area( Div ):
class_ = u"link_area_item",
),
( notebook.owner ) and Div(
A(
u"share",
href = u"#",
id = u"share_notebook_link",
title = u"Share this notebook with others.",
),
class_ = u"link_area_item",
) or None,
notebook.trash_id and Div(
A(
u"trash",