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 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 )

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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
)
);

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="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>

View File

@ -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",
),