Non-paying users can now invite people to their notebook, but only as viewers.
Paying users can invite them as viewers, collaborators, or owners.
This commit is contained in:
parent
5554b1df17
commit
52c111895a
3
NEWS
3
NEWS
|
@ -1,4 +1,5 @@
|
||||||
1.0.4: December ??, 2007
|
1.1.0: December ??, 2007
|
||||||
|
* Ability to invite people to your notebook.
|
||||||
* When the web browser is resized, all notes are automatically resized as well.
|
* When the web browser is resized, all notes are automatically resized as well.
|
||||||
* Fixed note focusing in Safari.
|
* Fixed note focusing in Safari.
|
||||||
* Fixed note state detection (bold, italic, etc.) in Safari.
|
* Fixed note state detection (bold, italic, etc.) in Safari.
|
||||||
|
|
|
@ -27,22 +27,22 @@ settings = {
|
||||||
{
|
{
|
||||||
"name": "free",
|
"name": "free",
|
||||||
"storage_quota_bytes": 30 * MEGABYTE,
|
"storage_quota_bytes": 30 * MEGABYTE,
|
||||||
"notebook_sharing": False,
|
"notebook_collaboration": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "basic",
|
"name": "basic",
|
||||||
"storage_quota_bytes": 250 * MEGABYTE,
|
"storage_quota_bytes": 250 * MEGABYTE,
|
||||||
"notebook_sharing": True,
|
"notebook_collaboration": True,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "standard",
|
"name": "standard",
|
||||||
"storage_quota_bytes": 500 * MEGABYTE,
|
"storage_quota_bytes": 500 * MEGABYTE,
|
||||||
"notebook_sharing": True,
|
"notebook_collaboration": True,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "premium",
|
"name": "premium",
|
||||||
"storage_quota_bytes": 1000 * MEGABYTE,
|
"storage_quota_bytes": 1000 * MEGABYTE,
|
||||||
"notebook_sharing": True,
|
"notebook_collaboration": True,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -668,7 +668,8 @@ class Users( object ):
|
||||||
@type email_addresses: unicode
|
@type email_addresses: unicode
|
||||||
@param email_addresses: a string containing whitespace- or comma-separated email addresses
|
@param email_addresses: a string containing whitespace- or comma-separated email addresses
|
||||||
@type access: unicode
|
@type access: unicode
|
||||||
@param access: type of access to grant, either "collaborator", "viewer", or "owner"
|
@param access: type of access to grant, either "collaborator", "viewer", or "owner". with
|
||||||
|
certain rate plans, only "viewer" is allowed
|
||||||
@type invite_button: unicode
|
@type invite_button: unicode
|
||||||
@param invite_button: ignored
|
@param invite_button: ignored
|
||||||
@type user_id: unicode
|
@type user_id: unicode
|
||||||
|
@ -677,13 +678,22 @@ class Users( object ):
|
||||||
@return: { 'message': message }
|
@return: { 'message': message }
|
||||||
@raise Password_reset_error: an error occured when sending the password reset email
|
@raise Password_reset_error: an error occured when sending the password reset email
|
||||||
@raise Validation_error: one of the arguments is invalid
|
@raise Validation_error: one of the arguments is invalid
|
||||||
@raise Access_error: user_id doesn't have owner-level notebook access to send an invite
|
@raise Access_error: user_id doesn't have owner-level notebook access to send an invite or
|
||||||
|
doesn't have a rate plan supporting notebook collaboration
|
||||||
"""
|
"""
|
||||||
if len( email_addresses ) < 5:
|
if len( email_addresses ) < 5:
|
||||||
raise Invite_error( u"Please enter at least one valid email address." )
|
raise Invite_error( u"Please enter at least one valid email address." )
|
||||||
if len( email_addresses ) > 5000:
|
if len( email_addresses ) > 5000:
|
||||||
raise Invite_error( u"Please enter fewer email addresses." )
|
raise Invite_error( u"Please enter fewer email addresses." )
|
||||||
|
|
||||||
|
if not self.check_access( user_id, notebook_id, read_write = True, owner = True ):
|
||||||
|
raise Access_error()
|
||||||
|
|
||||||
|
# this feature requires a rate plan above basic
|
||||||
|
user = self.__database.load( User, user_id )
|
||||||
|
if user is None or ( user.rate_plan == 0 and access != u"viewer" ):
|
||||||
|
raise Access_error()
|
||||||
|
|
||||||
if access == u"collaborator":
|
if access == u"collaborator":
|
||||||
read_write = True
|
read_write = True
|
||||||
owner = False
|
owner = False
|
||||||
|
@ -696,14 +706,6 @@ class Users( object ):
|
||||||
else:
|
else:
|
||||||
raise Access_error()
|
raise Access_error()
|
||||||
|
|
||||||
if not self.check_access( user_id, notebook_id, read_write = True, owner = True ):
|
|
||||||
raise Access_error()
|
|
||||||
|
|
||||||
# this feature requires a rate plan above basic
|
|
||||||
user = self.__database.load( User, user_id )
|
|
||||||
if user is None or user.rate_plan == 0:
|
|
||||||
raise Access_error()
|
|
||||||
|
|
||||||
notebook = self.__database.load( Notebook, notebook_id )
|
notebook = self.__database.load( Notebook, notebook_id )
|
||||||
if notebook is None:
|
if notebook is None:
|
||||||
raise Access_error()
|
raise Access_error()
|
||||||
|
|
|
@ -1180,7 +1180,7 @@ class Test_users( Test_controller ):
|
||||||
assert result[ u"error" ]
|
assert result[ u"error" ]
|
||||||
assert "access" in result[ u"error" ]
|
assert "access" in result[ u"error" ]
|
||||||
|
|
||||||
def test_send_invites_without_sufficient_rate_plan( self ):
|
def test_send_invites_viewer_with_lowest_rate_plan( self ):
|
||||||
Stub_smtp.reset()
|
Stub_smtp.reset()
|
||||||
smtplib.SMTP = Stub_smtp
|
smtplib.SMTP = Stub_smtp
|
||||||
self.login()
|
self.login()
|
||||||
|
@ -1196,6 +1196,51 @@ class Test_users( Test_controller ):
|
||||||
), session_id = self.session_id )
|
), session_id = self.session_id )
|
||||||
session_id = result[ u"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
|
||||||
|
assert self.INVITE_LINK_PATTERN.search( message )
|
||||||
|
|
||||||
|
def test_send_invites_collaborator_with_lowest_rate_plan( self ):
|
||||||
|
Stub_smtp.reset()
|
||||||
|
smtplib.SMTP = Stub_smtp
|
||||||
|
self.login()
|
||||||
|
|
||||||
|
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 result[ u"error" ]
|
||||||
|
assert "access" in result[ u"error" ]
|
||||||
|
|
||||||
|
def test_send_invites_owner_with_lowest_rate_plan( self ):
|
||||||
|
Stub_smtp.reset()
|
||||||
|
smtplib.SMTP = Stub_smtp
|
||||||
|
self.login()
|
||||||
|
|
||||||
|
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 result[ u"error" ]
|
assert result[ u"error" ]
|
||||||
assert "access" in result[ u"error" ]
|
assert "access" in result[ u"error" ]
|
||||||
|
|
||||||
|
|
|
@ -185,23 +185,25 @@ Editor.prototype.finish_init = function () {
|
||||||
|
|
||||||
var invite_button = getElement( "invite_button" );
|
var invite_button = getElement( "invite_button" );
|
||||||
if ( invite_button ) {
|
if ( invite_button ) {
|
||||||
var collaborators_radio = getElement( "collaborators_radio" );
|
if ( getElement( "access_choices" ) ) {
|
||||||
connect( "collaborators_link", "onclick", function ( event ) {
|
var collaborators_radio = getElement( "collaborators_radio" );
|
||||||
collaborators_radio.checked = true;
|
connect( "collaborators_link", "onclick", function ( event ) {
|
||||||
event.stop();
|
collaborators_radio.checked = true;
|
||||||
} );
|
event.stop();
|
||||||
|
} );
|
||||||
|
|
||||||
var viewers_radio = getElement( "viewers_radio" );
|
var viewers_radio = getElement( "viewers_radio" );
|
||||||
connect( "viewers_link", "onclick", function ( event ) {
|
connect( "viewers_link", "onclick", function ( event ) {
|
||||||
viewers_radio.checked = true;
|
viewers_radio.checked = true;
|
||||||
event.stop();
|
event.stop();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
var owners_radio = getElement( "owners_radio" );
|
var owners_radio = getElement( "owners_radio" );
|
||||||
connect( "owners_link", "onclick", function ( event ) {
|
connect( "owners_link", "onclick", function ( event ) {
|
||||||
owners_radio.checked = true;
|
owners_radio.checked = true;
|
||||||
event.stop();
|
event.stop();
|
||||||
} );
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
var invite_form = getElement( "invite_form" );
|
var invite_form = getElement( "invite_form" );
|
||||||
connect( invite_button, "onclick", function ( event ) {
|
connect( invite_button, "onclick", function ( event ) {
|
||||||
|
|
|
@ -1250,15 +1250,6 @@ Wiki.prototype.share_notebook = function () {
|
||||||
return;
|
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",
|
var collaborators_link = createDOM( "a",
|
||||||
{ "href": "#", "id": "collaborators_link", "class": "radio_link", "title": "Collaborators may view and edit this notebook." },
|
{ "href": "#", "id": "collaborators_link", "class": "radio_link", "title": "Collaborators may view and edit this notebook." },
|
||||||
"collaborators"
|
"collaborators"
|
||||||
|
@ -1282,6 +1273,28 @@ Wiki.prototype.share_notebook = function () {
|
||||||
{ "type": "radio", "id": "owners_radio", "name": "access", "value": "owner" }
|
{ "type": "radio", "id": "owners_radio", "name": "access", "value": "owner" }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ( this.rate_plan.notebook_collaboration ) {
|
||||||
|
var access_area = createDOM( "p", { "id": "access_choices" },
|
||||||
|
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 )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
var access_area = createDOM( "p", {},
|
||||||
|
createDOM( "b", {}, "Note: " ),
|
||||||
|
"These people will only be able to view your notebook. If you'd like them to be able to edit ",
|
||||||
|
"your notebook as well, please ",
|
||||||
|
createDOM( "a", { "href": "/upgrade", "target": "_new" }, "upgrade" ),
|
||||||
|
" your account.",
|
||||||
|
createDOM( "input", { "type": "hidden", "name": "access", "value": "viewer" } )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
var div = createDOM( "div", {},
|
var div = createDOM( "div", {},
|
||||||
createDOM( "form", { "id": "invite_form" },
|
createDOM( "form", { "id": "invite_form" },
|
||||||
createDOM( "input", { "type": "hidden", "name": "notebook_id", "value": this.notebook_id } ),
|
createDOM( "input", { "type": "hidden", "name": "notebook_id", "value": this.notebook_id } ),
|
||||||
|
@ -1293,16 +1306,7 @@ Wiki.prototype.share_notebook = function () {
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
createDOM( "p", {}, "Please separate email addresses with commas, spaces, or the enter key." ),
|
createDOM( "p", {}, "Please separate email addresses with commas, spaces, or the enter key." ),
|
||||||
createDOM( "p", {},
|
access_area,
|
||||||
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( "p", {},
|
||||||
createDOM( "input",
|
createDOM( "input",
|
||||||
{ "type": "submit", "name": "invite_button", "id": "invite_button", "class": "button", "value": "send invites" }
|
{ "type": "submit", "name": "invite_button", "id": "invite_button", "class": "button", "value": "send invites" }
|
||||||
|
|
Reference in New Issue