* model.Invite now has a to_dict() method, like other model classes do.
* Initial invites are now returned through Main_page() and displayed (but not yet updated) within the "share this notebook" note.
This commit is contained in:
parent
a4a6667642
commit
225167035d
|
@ -9,6 +9,7 @@ from Expire import strongly_expire
|
|||
from Html_nuker import Html_nuker
|
||||
from model.Notebook import Notebook
|
||||
from model.Note import Note
|
||||
from model.Invite import Invite
|
||||
from model.User import User
|
||||
from view.Main_page import Main_page
|
||||
from view.Json import Json
|
||||
|
@ -152,12 +153,14 @@ class Notebooks( object ):
|
|||
|
||||
startup_notes = self.__database.select_many( Note, notebook.sql_load_startup_notes() )
|
||||
total_notes_count = self.__database.select_one( int, notebook.sql_count_notes() )
|
||||
invites = self.__database.select_many( Invite, Invite.sql_load_notebook_invites( notebook_id ) )
|
||||
|
||||
return dict(
|
||||
notebook = notebook,
|
||||
startup_notes = startup_notes,
|
||||
total_notes_count = total_notes_count,
|
||||
notes = note and [ note ] or [],
|
||||
invites = invites or [],
|
||||
)
|
||||
|
||||
@expose( view = Json )
|
||||
|
|
|
@ -240,9 +240,9 @@ class Root( object ):
|
|||
from email import Message
|
||||
|
||||
message = Message.Message()
|
||||
message[ u"from" ] = support_email
|
||||
message[ u"to" ] = support_email
|
||||
message[ u"subject" ] = u"Luminotes traceback"
|
||||
message[ u"From" ] = support_email
|
||||
message[ u"To" ] = support_email
|
||||
message[ u"Subject" ] = u"Luminotes traceback"
|
||||
message.set_payload(
|
||||
u"requested URL: %s\n" % cherrypy.request.browser_url +
|
||||
u"user id: %s\n" % cherrypy.session.get( "user_id" ) +
|
||||
|
@ -253,7 +253,7 @@ class Root( object ):
|
|||
# send the message out through localhost's smtp server
|
||||
server = smtplib.SMTP()
|
||||
server.connect()
|
||||
server.sendmail( message[ u"from" ], [ support_email ], message.as_string() )
|
||||
server.sendmail( message[ u"From" ], [ support_email ], message.as_string() )
|
||||
server.quit()
|
||||
|
||||
return True
|
||||
|
|
|
@ -578,6 +578,7 @@ class Users( object ):
|
|||
contents = unicode( Redeem_reset_note( password_reset_id, matching_users ) ),
|
||||
notebook_id = main_notebook.object_id,
|
||||
) ]
|
||||
result[ "invites" ] = []
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
@ -230,6 +230,20 @@ class Test_controller( object ):
|
|||
Invite.sql_load_similar = lambda self: \
|
||||
lambda database: sql_load_similar( self, database )
|
||||
|
||||
def sql_load_notebook_invites( notebook_id, database ):
|
||||
invites = []
|
||||
|
||||
for ( object_id, obj_list ) in database.objects.items():
|
||||
obj = obj_list[ -1 ]
|
||||
if isinstance( obj, Invite ) and obj.notebook_id == notebook_id and \
|
||||
obj.email_address not in [ i.email_address for i in invites ]:
|
||||
invites.append( obj )
|
||||
|
||||
return invites
|
||||
|
||||
Invite.sql_load_notebook_invites = staticmethod( lambda notebook_id:
|
||||
lambda database: sql_load_notebook_invites( notebook_id, database ) )
|
||||
|
||||
def setUp( self ):
|
||||
from controller.Root import Root
|
||||
cherrypy.lowercase_api = True
|
||||
|
|
|
@ -6,6 +6,7 @@ from Test_controller import Test_controller
|
|||
from model.Notebook import Notebook
|
||||
from model.Note import Note
|
||||
from model.User import User
|
||||
from model.Invite import Invite
|
||||
from controller.Notebooks import Access_error
|
||||
|
||||
|
||||
|
@ -21,6 +22,7 @@ class Test_notebooks( Test_controller ):
|
|||
self.password = u"trustno1"
|
||||
self.email_address = u"outthere@example.com"
|
||||
self.user = None
|
||||
self.invite = None
|
||||
self.anonymous = None
|
||||
self.session_id = None
|
||||
|
||||
|
@ -55,6 +57,12 @@ class Test_notebooks( Test_controller ):
|
|||
self.database.save( self.anonymous, commit = False )
|
||||
self.database.execute( self.user.sql_save_notebook( self.anon_notebook.object_id, read_write = False, owner = False ) )
|
||||
|
||||
self.invite = Invite.create(
|
||||
self.database.next_id( Invite ), self.user.object_id, self.notebook.object_id,
|
||||
u"skinner@example.com", read_write = True, owner = False,
|
||||
)
|
||||
self.database.save( self.invite, commit = False )
|
||||
|
||||
def test_default_without_login( self ):
|
||||
result = self.http_get(
|
||||
"/notebooks/%s" % self.notebook.object_id,
|
||||
|
@ -84,6 +92,11 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"parent_id" ) == None
|
||||
assert result.get( u"note_read_write" ) in ( None, True )
|
||||
|
||||
invites = result[ "invites" ]
|
||||
assert len( invites ) == 1
|
||||
invite = invites[ 0 ]
|
||||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == 0
|
||||
|
||||
|
@ -110,6 +123,11 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"parent_id" ) == None
|
||||
assert result.get( u"note_read_write" ) in ( None, True )
|
||||
|
||||
invites = result[ "invites" ]
|
||||
assert len( invites ) == 1
|
||||
invite = invites[ 0 ]
|
||||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == 0
|
||||
|
||||
|
@ -141,6 +159,11 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"parent_id" ) == None
|
||||
assert result.get( u"note_read_write" ) == False
|
||||
|
||||
invites = result[ "invites" ]
|
||||
assert len( invites ) == 1
|
||||
invite = invites[ 0 ]
|
||||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == 0
|
||||
|
||||
|
@ -165,6 +188,11 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"parent_id" ) == parent_id
|
||||
assert result.get( u"note_read_write" ) in ( None, True )
|
||||
|
||||
invites = result[ "invites" ]
|
||||
assert len( invites ) == 1
|
||||
invite = invites[ 0 ]
|
||||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == 0
|
||||
|
||||
|
@ -179,6 +207,11 @@ class Test_notebooks( Test_controller ):
|
|||
assert result[ "total_notes_count" ] == 2
|
||||
assert result[ "notes" ] == []
|
||||
|
||||
invites = result[ "invites" ]
|
||||
assert len( invites ) == 1
|
||||
invite = invites[ 0 ]
|
||||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.owner == True
|
||||
|
@ -198,6 +231,11 @@ class Test_notebooks( Test_controller ):
|
|||
startup_notes = result[ "startup_notes" ]
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
||||
invites = result[ "invites" ]
|
||||
assert len( invites ) == 1
|
||||
invite = invites[ 0 ]
|
||||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.owner == True
|
||||
|
@ -226,6 +264,11 @@ class Test_notebooks( Test_controller ):
|
|||
startup_notes = result[ "startup_notes" ]
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
||||
invites = result[ "invites" ]
|
||||
assert len( invites ) == 1
|
||||
invite = invites[ 0 ]
|
||||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.owner == True
|
||||
|
@ -242,6 +285,71 @@ class Test_notebooks( Test_controller ):
|
|||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == 0
|
||||
|
||||
def test_contents_with_different_invites( self ):
|
||||
# create an invite with a different email address from the previous
|
||||
invite = Invite.create(
|
||||
self.database.next_id( Invite ), self.user.object_id, self.notebook.object_id,
|
||||
u"smoking@example.com", read_write = True, owner = False,
|
||||
)
|
||||
self.database.save( invite )
|
||||
|
||||
result = cherrypy.root.notebooks.contents(
|
||||
notebook_id = self.notebook.object_id,
|
||||
user_id = self.user.object_id,
|
||||
)
|
||||
|
||||
notebook = result[ "notebook" ]
|
||||
startup_notes = result[ "startup_notes" ]
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
assert result[ "notes" ] == []
|
||||
|
||||
invites = result[ "invites" ]
|
||||
assert len( invites ) == 2
|
||||
invite = invites[ 0 ]
|
||||
assert invite.object_id == invite.object_id
|
||||
invite = invites[ 1 ]
|
||||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.owner == True
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == 0
|
||||
|
||||
def test_contents_with_duplicate_invites( self ):
|
||||
# create an invite with the same email address as the previous invite
|
||||
invite = Invite.create(
|
||||
self.database.next_id( Invite ), self.user.object_id, self.notebook.object_id,
|
||||
u"skinner@example.com", read_write = True, owner = False,
|
||||
)
|
||||
self.database.save( invite )
|
||||
|
||||
result = cherrypy.root.notebooks.contents(
|
||||
notebook_id = self.notebook.object_id,
|
||||
user_id = self.user.object_id,
|
||||
)
|
||||
|
||||
notebook = result[ "notebook" ]
|
||||
startup_notes = result[ "startup_notes" ]
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
assert result[ "notes" ] == []
|
||||
|
||||
# the two invites should be collapsed down into one
|
||||
invites = result[ "invites" ]
|
||||
assert len( invites ) == 1
|
||||
invite = invites[ 0 ]
|
||||
assert invite.object_id == invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.owner == True
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == 0
|
||||
|
||||
@raises( Access_error )
|
||||
def test_contents_without_user_id( self ):
|
||||
result = cherrypy.root.notebooks.contents(
|
||||
|
@ -272,6 +380,7 @@ class Test_notebooks( Test_controller ):
|
|||
startup_notes = result[ "startup_notes" ]
|
||||
assert result[ "notes" ] == []
|
||||
assert result[ "total_notes_count" ] == 0
|
||||
assert result[ "invites" ] == []
|
||||
|
||||
assert notebook.object_id == self.anon_notebook.object_id
|
||||
assert notebook.read_write == False
|
||||
|
@ -1762,6 +1871,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert result[ "total_notes_count" ] == 0
|
||||
assert result[ "startup_notes" ] == []
|
||||
assert result[ "notes" ] == []
|
||||
assert result[ "invites" ] == []
|
||||
|
||||
assert notebook.object_id == new_notebook_id
|
||||
assert notebook.read_write == True
|
||||
|
|
|
@ -91,11 +91,31 @@ class Invite( Persistent ):
|
|||
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
|
||||
# select unredeemed invites with the same from_user_id, notebook_id, and email_address as this invite
|
||||
return "select id, revision, from_user_id, notebook_id, email_address, read_write, owner, redeemed_user_id from invite " + \
|
||||
"where from_user_id = %s and notebook_id = %s and email_address = %s and id != %s and redeemed_user_id is null;" % \
|
||||
( quote( self.__from_user_id ), quote( self.__notebook_id ), quote( self.__email_address ), quote( self.object_id ) )
|
||||
|
||||
@staticmethod
|
||||
def sql_load_notebook_invites( notebook_id ):
|
||||
# select a list of invites to the given notebook. if there are multiple invites for a given
|
||||
# email_address, arbitrarily pick one of them
|
||||
return "select id, revision, from_user_id, notebook_id, email_address, read_write, owner, redeemed_user_id from invite " + \
|
||||
"where id in ( select max( id ) from invite where notebook_id = %s group by email_address ) order by email_address;" % quote( notebook_id )
|
||||
|
||||
def to_dict( self ):
|
||||
d = Persistent.to_dict( self )
|
||||
d.update( dict(
|
||||
from_user_id = self.__from_user_id,
|
||||
notebook_id = self.__notebook_id,
|
||||
email_address = self.__email_address,
|
||||
read_write = self.__read_write,
|
||||
owner = self.__owner,
|
||||
redeemed_user_id = self.__redeemed_user_id,
|
||||
) )
|
||||
|
||||
return d
|
||||
|
||||
def __set_read_write( self, read_write ):
|
||||
if read_write != self.__read_write:
|
||||
self.update_revision()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from pytz import utc
|
||||
from datetime import datetime, timedelta
|
||||
from model.User import User
|
||||
from model.Invite import Invite
|
||||
|
||||
|
@ -10,6 +12,7 @@ class Test_invite( object ):
|
|||
self.email_address = u"bob@example.com"
|
||||
self.read_write = True
|
||||
self.owner = False
|
||||
self.delta = timedelta( seconds = 1 )
|
||||
|
||||
self.invite = Invite.create( self.object_id, self.from_user_id, self.notebook_id,
|
||||
self.email_address, self.read_write, self.owner )
|
||||
|
@ -39,3 +42,14 @@ class Test_invite( object ):
|
|||
|
||||
assert self.invite.redeemed_user_id == redeemed_user_id
|
||||
assert self.invite.revision == current_revision
|
||||
|
||||
def test_to_dict( self ):
|
||||
d = self.invite.to_dict()
|
||||
|
||||
assert d.get( "object_id" ) == self.object_id
|
||||
assert datetime.now( tz = utc ) - d.get( "revision" ) < self.delta
|
||||
assert d.get( "from_user_id" ) == self.from_user_id
|
||||
assert d.get( "notebook_id" ) == self.notebook_id
|
||||
assert d.get( "read_write" ) == self.read_write
|
||||
assert d.get( "owner" ) == self.owner
|
||||
assert d.get( "redeemed_user_id" ) == None
|
||||
|
|
|
@ -12,6 +12,7 @@ function Wiki( invoker ) {
|
|||
this.invoker = invoker;
|
||||
this.rate_plan = evalJSON( getElement( "rate_plan" ).value );
|
||||
this.storage_usage_high = false;
|
||||
this.invites = evalJSON( getElement( "invites" ).value );
|
||||
|
||||
var total_notes_count_node = getElement( "total_notes_count" );
|
||||
if ( total_notes_count_node )
|
||||
|
@ -1296,6 +1297,41 @@ Wiki.prototype.share_notebook = function () {
|
|||
);
|
||||
}
|
||||
|
||||
if ( this.invites ) {
|
||||
var collaborators = createDOM( "ul", { "id": "collaborators" } );
|
||||
var viewers = createDOM( "ul", { "id": "viewers" } );
|
||||
var owners = createDOM( "ul", { "id": "owners" } );
|
||||
|
||||
for ( var i in this.invites ) {
|
||||
var invite = this.invites[ i ];
|
||||
if ( invite.owner ) {
|
||||
appendChildNodes( owners, createDOM( "li", {}, invite.email_address ) );
|
||||
} else {
|
||||
if ( invite.read_write )
|
||||
appendChildNodes( collaborators, createDOM( "li", {}, invite.email_address ) );
|
||||
else
|
||||
appendChildNodes( viewers, createDOM( "li", {}, invite.email_address ) );
|
||||
}
|
||||
}
|
||||
|
||||
var invite_area = createDOM( "p", { "id": "invite_area" } );
|
||||
|
||||
if ( collaborators.childNodes.length > 0 ) {
|
||||
appendChildNodes( invite_area, createDOM( "h3", {}, "collaborators" ) );
|
||||
appendChildNodes( invite_area, collaborators );
|
||||
}
|
||||
if ( viewers.childNodes.length > 0 ) {
|
||||
appendChildNodes( invite_area, createDOM( "h3", {}, "viewers" ) );
|
||||
appendChildNodes( invite_area, viewers );
|
||||
}
|
||||
if ( owners.childNodes.length > 0 ) {
|
||||
appendChildNodes( invite_area, createDOM( "h3", {}, "owners" ) );
|
||||
appendChildNodes( invite_area, owners );
|
||||
}
|
||||
} else {
|
||||
var invite_area = createDOM( "p", {}, "There are no invites." );
|
||||
}
|
||||
|
||||
var div = createDOM( "div", {},
|
||||
createDOM( "form", { "id": "invite_form" },
|
||||
createDOM( "input", { "type": "hidden", "name": "notebook_id", "value": this.notebook_id } ),
|
||||
|
@ -1312,7 +1348,8 @@ Wiki.prototype.share_notebook = function () {
|
|||
createDOM( "input",
|
||||
{ "type": "submit", "name": "invite_button", "id": "invite_button", "class": "button", "value": "send invites" }
|
||||
)
|
||||
)
|
||||
),
|
||||
invite_area
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ function test_Wiki() {
|
|||
<input type="hidden" name="note_read_write" id="note_read_write" value="" />
|
||||
<input type="hidden" name="rename" id="rename" value="false" />
|
||||
<input type="hidden" name="deleted_id" id="deleted_id" value="" />
|
||||
<input type="hidden" name="invited" id="invited" value="[]" />
|
||||
|
||||
<div id="static_notes">
|
||||
</div>
|
||||
|
|
|
@ -29,6 +29,7 @@ class Main_page( Page ):
|
|||
conversion = None,
|
||||
rename = False,
|
||||
deleted_id = None,
|
||||
invites = None,
|
||||
):
|
||||
startup_note_ids = [ startup_note.object_id for startup_note in startup_notes ]
|
||||
|
||||
|
@ -98,6 +99,7 @@ class Main_page( Page ):
|
|||
Input( type = u"hidden", name = u"note_read_write", id = u"note_read_write", value = json( note_read_write ) ),
|
||||
Input( type = u"hidden", name = u"rename", id = u"rename", value = json( rename ) ),
|
||||
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 ) ),
|
||||
Div(
|
||||
id = u"status_area",
|
||||
),
|
||||
|
|
Reference in New Issue