witten
/
luminotes
Archived
1
0
Fork 0

* 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:
Dan Helfman 2007-12-12 00:52:34 +00:00
parent a4a6667642
commit 225167035d
10 changed files with 208 additions and 6 deletions

View File

@ -9,6 +9,7 @@ from Expire import strongly_expire
from Html_nuker import Html_nuker from Html_nuker import Html_nuker
from model.Notebook import Notebook from model.Notebook import Notebook
from model.Note import Note from model.Note import Note
from model.Invite import Invite
from model.User import User from model.User import User
from view.Main_page import Main_page from view.Main_page import Main_page
from view.Json import Json from view.Json import Json
@ -152,12 +153,14 @@ class Notebooks( object ):
startup_notes = self.__database.select_many( Note, notebook.sql_load_startup_notes() ) startup_notes = self.__database.select_many( Note, notebook.sql_load_startup_notes() )
total_notes_count = self.__database.select_one( int, notebook.sql_count_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( return dict(
notebook = notebook, notebook = notebook,
startup_notes = startup_notes, startup_notes = startup_notes,
total_notes_count = total_notes_count, total_notes_count = total_notes_count,
notes = note and [ note ] or [], notes = note and [ note ] or [],
invites = invites or [],
) )
@expose( view = Json ) @expose( view = Json )

View File

@ -240,9 +240,9 @@ class Root( object ):
from email import Message from email import Message
message = Message.Message() message = Message.Message()
message[ u"from" ] = support_email message[ u"From" ] = support_email
message[ u"to" ] = support_email message[ u"To" ] = support_email
message[ u"subject" ] = u"Luminotes traceback" message[ u"Subject" ] = u"Luminotes traceback"
message.set_payload( message.set_payload(
u"requested URL: %s\n" % cherrypy.request.browser_url + u"requested URL: %s\n" % cherrypy.request.browser_url +
u"user id: %s\n" % cherrypy.session.get( "user_id" ) + 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 # send the message out through localhost's smtp server
server = smtplib.SMTP() server = smtplib.SMTP()
server.connect() server.connect()
server.sendmail( message[ u"from" ], [ support_email ], message.as_string() ) server.sendmail( message[ u"From" ], [ support_email ], message.as_string() )
server.quit() server.quit()
return True return True

View File

@ -578,6 +578,7 @@ class Users( object ):
contents = unicode( Redeem_reset_note( password_reset_id, matching_users ) ), contents = unicode( Redeem_reset_note( password_reset_id, matching_users ) ),
notebook_id = main_notebook.object_id, notebook_id = main_notebook.object_id,
) ] ) ]
result[ "invites" ] = []
return result return result

View File

@ -230,6 +230,20 @@ class Test_controller( object ):
Invite.sql_load_similar = lambda self: \ Invite.sql_load_similar = lambda self: \
lambda database: sql_load_similar( self, database ) 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 ): def setUp( self ):
from controller.Root import Root from controller.Root import Root
cherrypy.lowercase_api = True cherrypy.lowercase_api = True

View File

@ -6,6 +6,7 @@ from Test_controller import Test_controller
from model.Notebook import Notebook from model.Notebook import Notebook
from model.Note import Note from model.Note import Note
from model.User import User from model.User import User
from model.Invite import Invite
from controller.Notebooks import Access_error from controller.Notebooks import Access_error
@ -21,6 +22,7 @@ class Test_notebooks( Test_controller ):
self.password = u"trustno1" self.password = u"trustno1"
self.email_address = u"outthere@example.com" self.email_address = u"outthere@example.com"
self.user = None self.user = None
self.invite = None
self.anonymous = None self.anonymous = None
self.session_id = None self.session_id = None
@ -55,6 +57,12 @@ class Test_notebooks( Test_controller ):
self.database.save( self.anonymous, commit = False ) 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.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 ): def test_default_without_login( self ):
result = self.http_get( result = self.http_get(
"/notebooks/%s" % self.notebook.object_id, "/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"parent_id" ) == None
assert result.get( u"note_read_write" ) in ( None, True ) 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 ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0 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"parent_id" ) == None
assert result.get( u"note_read_write" ) in ( None, True ) 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 ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0 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"parent_id" ) == None
assert result.get( u"note_read_write" ) == False 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 ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0 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"parent_id" ) == parent_id
assert result.get( u"note_read_write" ) in ( None, True ) 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 ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0 assert user.storage_bytes == 0
@ -179,6 +207,11 @@ class Test_notebooks( Test_controller ):
assert result[ "total_notes_count" ] == 2 assert result[ "total_notes_count" ] == 2
assert result[ "notes" ] == [] 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.object_id == self.notebook.object_id
assert notebook.read_write == True assert notebook.read_write == True
assert notebook.owner == True assert notebook.owner == True
@ -198,6 +231,11 @@ class Test_notebooks( Test_controller ):
startup_notes = result[ "startup_notes" ] startup_notes = result[ "startup_notes" ]
assert result[ "total_notes_count" ] == 2 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.object_id == self.notebook.object_id
assert notebook.read_write == True assert notebook.read_write == True
assert notebook.owner == True assert notebook.owner == True
@ -226,6 +264,11 @@ class Test_notebooks( Test_controller ):
startup_notes = result[ "startup_notes" ] startup_notes = result[ "startup_notes" ]
assert result[ "total_notes_count" ] == 2 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.object_id == self.notebook.object_id
assert notebook.read_write == True assert notebook.read_write == True
assert notebook.owner == True assert notebook.owner == True
@ -242,6 +285,71 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0 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 ) @raises( Access_error )
def test_contents_without_user_id( self ): def test_contents_without_user_id( self ):
result = cherrypy.root.notebooks.contents( result = cherrypy.root.notebooks.contents(
@ -272,6 +380,7 @@ class Test_notebooks( Test_controller ):
startup_notes = result[ "startup_notes" ] startup_notes = result[ "startup_notes" ]
assert result[ "notes" ] == [] assert result[ "notes" ] == []
assert result[ "total_notes_count" ] == 0 assert result[ "total_notes_count" ] == 0
assert result[ "invites" ] == []
assert notebook.object_id == self.anon_notebook.object_id assert notebook.object_id == self.anon_notebook.object_id
assert notebook.read_write == False assert notebook.read_write == False
@ -1762,6 +1871,7 @@ class Test_notebooks( Test_controller ):
assert result[ "total_notes_count" ] == 0 assert result[ "total_notes_count" ] == 0
assert result[ "startup_notes" ] == [] assert result[ "startup_notes" ] == []
assert result[ "notes" ] == [] assert result[ "notes" ] == []
assert result[ "invites" ] == []
assert notebook.object_id == new_notebook_id assert notebook.object_id == new_notebook_id
assert notebook.read_write == True assert notebook.read_write == True

View File

@ -91,11 +91,31 @@ class Invite( Persistent ):
quote( self.__redeemed_user_id ), quote( self.object_id ) ) quote( self.__redeemed_user_id ), quote( self.object_id ) )
def sql_load_similar( self ): 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 " + \ 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;" % \ "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 ) ) ( 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 ): def __set_read_write( self, read_write ):
if read_write != self.__read_write: if read_write != self.__read_write:
self.update_revision() self.update_revision()

View File

@ -1,3 +1,5 @@
from pytz import utc
from datetime import datetime, timedelta
from model.User import User from model.User import User
from model.Invite import Invite from model.Invite import Invite
@ -10,6 +12,7 @@ class Test_invite( object ):
self.email_address = u"bob@example.com" self.email_address = u"bob@example.com"
self.read_write = True self.read_write = True
self.owner = False self.owner = False
self.delta = timedelta( seconds = 1 )
self.invite = Invite.create( self.object_id, self.from_user_id, self.notebook_id, self.invite = Invite.create( self.object_id, self.from_user_id, self.notebook_id,
self.email_address, self.read_write, self.owner ) 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.redeemed_user_id == redeemed_user_id
assert self.invite.revision == current_revision 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

View File

@ -12,6 +12,7 @@ function Wiki( invoker ) {
this.invoker = invoker; this.invoker = invoker;
this.rate_plan = evalJSON( getElement( "rate_plan" ).value ); this.rate_plan = evalJSON( getElement( "rate_plan" ).value );
this.storage_usage_high = false; this.storage_usage_high = false;
this.invites = evalJSON( getElement( "invites" ).value );
var total_notes_count_node = getElement( "total_notes_count" ); var total_notes_count_node = getElement( "total_notes_count" );
if ( total_notes_count_node ) 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", {}, 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 } ),
@ -1312,7 +1348,8 @@ Wiki.prototype.share_notebook = function () {
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" }
) )
) ),
invite_area
) )
); );

View File

@ -85,6 +85,7 @@ function test_Wiki() {
<input type="hidden" name="note_read_write" id="note_read_write" value="" /> <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="rename" id="rename" value="false" />
<input type="hidden" name="deleted_id" id="deleted_id" value="" /> <input type="hidden" name="deleted_id" id="deleted_id" value="" />
<input type="hidden" name="invited" id="invited" value="[]" />
<div id="static_notes"> <div id="static_notes">
</div> </div>

View File

@ -29,6 +29,7 @@ class Main_page( Page ):
conversion = None, conversion = None,
rename = False, rename = False,
deleted_id = None, deleted_id = None,
invites = None,
): ):
startup_note_ids = [ startup_note.object_id for startup_note in startup_notes ] 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"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"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"deleted_id", id = u"deleted_id", value = deleted_id ),
Input( type = u"hidden", name = u"invites", id = u"invites", value = json( invites ) ),
Div( Div(
id = u"status_area", id = u"status_area",
), ),