From 388f2fcb021df73ae512714b44a63e8499eb5081 Mon Sep 17 00:00:00 2001
From: Dan Helfman new title
new blah"
+ result = self.http_post( "/notebooks/save_note/", dict(
+ notebook_id = self.notebook.object_id,
+ note_id = self.note.object_id,
+ contents = new_note_contents,
+ startup = False,
+ previous_revision = previous_revision,
+ ), session_id = self.session_id )
+
+ assert result[ "new_revision" ]
+ assert result[ "new_revision" ].revision != previous_revision
+ assert result[ "new_revision" ].user_id == self.user.object_id
+ assert result[ "new_revision" ].username == self.username
+ current_revision = result[ "new_revision" ].revision
+ assert result[ "previous_revision" ].revision == previous_revision
+ assert result[ "previous_revision" ].user_id == self.user.object_id
+ assert result[ "previous_revision" ].username == self.username
+
+ # make sure the old title can no longer be loaded
+ result = self.http_post( "/notebooks/load_note_by_title/", dict(
+ notebook_id = self.notebook.object_id,
+ note_title = "my title",
+ ), session_id = self.session_id )
+
+ note = result[ "note" ]
+ assert note == None
+
+ # make sure the new title is now loadable
+ result = self.http_post( "/notebooks/load_note_by_title/", dict(
+ notebook_id = self.notebook.object_id,
+ note_title = "new title",
+ ), session_id = self.session_id )
+
+ note = result[ "note" ]
+
+ assert note.object_id == self.note.object_id
+ assert note.title == "new title"
+ assert note.contents == new_note_contents
+ assert note.startup == True # startup is forced to True in READ_WRITE_FOR_OWN_NOTES notebook
+ assert note.user_id == self.user.object_id
+ assert note.rank == 0
+
+ # make sure that the correct revisions are returned and are in chronological order
+ result = self.http_post( "/notebooks/load_note_revisions/", dict(
+ notebook_id = self.notebook.object_id,
+ note_id = self.note.object_id,
+ ), session_id = self.session_id )
+
+ revisions = result[ "revisions" ]
+ assert revisions != None
+ assert len( revisions ) == 3
+ assert revisions[ 1 ].revision == previous_revision
+ assert revisions[ 1 ].user_id == self.user.object_id
+ assert revisions[ 1 ].username == self.username
+ assert revisions[ 2 ].revision == current_revision
+ assert revisions[ 2 ].user_id == self.user.object_id
+ assert revisions[ 2 ].username == self.username
+
def test_save_note_by_different_user( self, startup = False ):
self.login2()
@@ -1796,6 +1866,27 @@ class Test_notebooks( Test_controller ):
assert revisions[ 2 ].user_id == self.user2.object_id
assert revisions[ 2 ].username == self.username2
+ def test_save_note_by_different_user_with_notebook_read_write_for_own_notes( self ):
+ self.login2()
+
+ self.database.execute( self.user2.sql_update_access(
+ self.notebook.object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = True,
+ ) )
+
+ previous_revision = self.note.revision
+ new_note_contents = u"new title
new blah"
+ result = self.http_post( "/notebooks/save_note/", dict(
+ notebook_id = self.notebook.object_id,
+ note_id = self.note.object_id,
+ contents = new_note_contents,
+ startup = False,
+ previous_revision = previous_revision,
+ ), session_id = self.session_id )
+
+ assert result.get( "error" )
+ user = self.database.load( User, self.user.object_id )
+ assert user.storage_bytes == 0
+
def test_save_note_without_login( self, startup = False ):
# save over an existing note supplying new contents and a new title
previous_revision = self.note.revision
@@ -3688,7 +3779,7 @@ class Test_notebooks( Test_controller ):
assert isinstance( notebook, Notebook )
assert notebook.object_id == new_notebook_id
assert notebook.name == u"new notebook"
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.trash_id
@@ -3715,7 +3806,7 @@ class Test_notebooks( Test_controller ):
assert result[ "invites" ] == []
assert notebook.object_id == new_notebook_id
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
def test_create_without_login( self ):
@@ -3810,7 +3901,7 @@ class Test_notebooks( Test_controller ):
assert isinstance( notebook, Notebook )
assert notebook.object_id == remaining_notebook_id
assert notebook.name == u"my notebook"
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.trash_id
assert notebook.user_id == self.user.object_id
@@ -3864,7 +3955,7 @@ class Test_notebooks( Test_controller ):
assert isinstance( notebook, Notebook )
assert notebook.object_id == remaining_notebook_id
assert notebook.name == u"my notebook"
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.trash_id
assert notebook.user_id == self.user.object_id
@@ -4009,7 +4100,7 @@ class Test_notebooks( Test_controller ):
assert isinstance( notebook, Notebook )
assert notebook.object_id == notebook_id
assert notebook.name == self.notebook.name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.trash_id
assert notebook.user_id == self.user.object_id
@@ -4069,7 +4160,7 @@ class Test_notebooks( Test_controller ):
assert isinstance( notebook, Notebook )
assert notebook.object_id == notebook_id
assert notebook.name == self.notebook.name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.trash_id
assert notebook.user_id == self.user.object_id
@@ -4511,7 +4602,7 @@ class Test_notebooks( Test_controller ):
assert notebook.name == u"imported notebook"
assert notebook.trash_id
- assert notebook.read_write is True
+ assert notebook.read_write is Notebook.READ_WRITE
assert notebook.owner is True
assert notebook.deleted is False
assert notebook.user_id == self.user.object_id
diff --git a/controller/test/Test_root.py b/controller/test/Test_root.py
index 81740f7..aa3dc50 100644
--- a/controller/test/Test_root.py
+++ b/controller/test/Test_root.py
@@ -62,10 +62,10 @@ class Test_root( Test_controller ):
self.anonymous = User.create( self.database.next_id( User ), u"anonymous" )
self.database.save( self.anonymous )
- self.database.execute( self.anonymous.sql_save_notebook( self.anon_notebook.object_id, read_write = False, owner = False ) )
- self.database.execute( self.anonymous.sql_save_notebook( self.blog_notebook.object_id, read_write = False, owner = False ) )
- self.database.execute( self.anonymous.sql_save_notebook( self.guide_notebook.object_id, read_write = False, owner = False ) )
- self.database.execute( self.anonymous.sql_save_notebook( self.privacy_notebook.object_id, read_write = False, owner = False ) )
+ self.database.execute( self.anonymous.sql_save_notebook( self.anon_notebook.object_id, read_write = False, owner = False, rank = 0 ) )
+ self.database.execute( self.anonymous.sql_save_notebook( self.blog_notebook.object_id, read_write = False, owner = False, rank = 1 ) )
+ self.database.execute( self.anonymous.sql_save_notebook( self.guide_notebook.object_id, read_write = False, owner = False, rank = 2 ) )
+ self.database.execute( self.anonymous.sql_save_notebook( self.privacy_notebook.object_id, read_write = False, owner = False, rank = 3 ) )
def test_index( self ):
result = self.http_get( "/" )
@@ -429,7 +429,7 @@ class Test_root( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.anon_notebook.object_id ][ 0 ]
assert notebook.object_id == self.anon_notebook.object_id
assert notebook.name == self.anon_notebook.name
- assert notebook.read_write == False
+ assert notebook.read_write == Notebook.READ_ONLY
assert notebook.owner == False
rate_plan = result[ u"rate_plan" ]
@@ -451,7 +451,7 @@ class Test_root( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebook.object_id ][ 0 ]
assert notebook.object_id == self.notebook.object_id
assert notebook.name == self.notebook.name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
rate_plan = result[ u"rate_plan" ]
diff --git a/controller/test/Test_users.py b/controller/test/Test_users.py
index 2d07fe5..bbe2c00 100644
--- a/controller/test/Test_users.py
+++ b/controller/test/Test_users.py
@@ -62,21 +62,23 @@ class Test_users( Test_controller ):
self.database.save( self.notebooks[ 0 ] )
self.database.save( self.notebooks[ 1 ] )
+ self.user = User.create( self.database.next_id( User ), self.username, self.password, self.email_address )
+ self.database.save( self.user, commit = False )
+
self.anon_notebook = Notebook.create( self.database.next_id( Notebook ), u"anon notebook" )
self.database.save( self.anon_notebook )
self.startup_note = Note.create(
self.database.next_id( Note ), u"login
",
notebook_id = self.anon_notebook.object_id, startup = True,
+ user_id = self.user.object_id,
)
- self.database.save( self.startup_note )
+ self.database.save( self.startup_note, commit = False )
self.group = Group.create( self.database.next_id( Group ), u"my group" )
self.database.save( self.group, commit = False )
self.group2 = Group.create( self.database.next_id( Group ), u"other group" )
self.database.save( self.group2, commit = False )
- self.user = User.create( self.database.next_id( User ), self.username, self.password, self.email_address )
- self.database.save( self.user, commit = False )
self.database.execute( self.user.sql_save_notebook( notebook_id1, read_write = True, owner = True, rank = 0 ), commit = False )
self.database.execute( self.user.sql_save_notebook( trash_id1, read_write = True, owner = True ), commit = False )
self.database.execute( self.user.sql_save_notebook( notebook_id2, read_write = True, owner = True, rank = 1 ), commit = False )
@@ -180,7 +182,7 @@ class Test_users( Test_controller ):
assert notebook.revision
assert notebook.name == u"trash"
assert notebook.trash_id == None
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == None
@@ -189,7 +191,7 @@ class Test_users( Test_controller ):
assert notebook.revision
assert notebook.name == u"my notebook"
assert notebook.trash_id
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -198,7 +200,7 @@ class Test_users( Test_controller ):
assert notebook.revision == self.anon_notebook.revision
assert notebook.name == self.anon_notebook.name
assert notebook.trash_id == None
- assert notebook.read_write == False
+ assert notebook.read_write == Notebook.READ_ONLY
assert notebook.owner == False
assert notebook.rank == None
@@ -250,8 +252,8 @@ class Test_users( Test_controller ):
assert result[ u"user" ].username == self.new_username
assert result[ u"user" ].email_address == self.new_email_address
- assert cherrypy.root.users.check_access( user.object_id, self.notebooks[ 0 ].object_id )
- assert cherrypy.root.users.check_access( user.object_id, self.notebooks[ 0 ].trash_id )
+ assert cherrypy.root.users.load_notebook( user.object_id, self.notebooks[ 0 ].object_id )
+ assert cherrypy.root.users.load_notebook( user.object_id, self.notebooks[ 0 ].trash_id )
# the notebook that the user was invited to should be in the list of returned notebooks
notebooks = dict( [ ( notebook.object_id, notebook ) for notebook in result[ u"notebooks" ] ] )
@@ -261,7 +263,7 @@ class Test_users( Test_controller ):
assert notebook.revision
assert notebook.name == self.notebooks[ 0 ].name
assert notebook.trash_id
- assert notebook.read_write == False
+ assert notebook.read_write == Notebook.READ_ONLY
assert notebook.owner == False
assert notebook.rank == 1
@@ -269,7 +271,7 @@ class Test_users( Test_controller ):
assert notebook.revision
assert notebook.name == u"trash"
assert notebook.trash_id == None
- assert notebook.read_write == False
+ assert notebook.read_write == Notebook.READ_ONLY
assert notebook.owner == False
assert notebook.rank == None
@@ -277,7 +279,7 @@ class Test_users( Test_controller ):
assert notebook.revision == self.anon_notebook.revision
assert notebook.name == self.anon_notebook.name
assert notebook.trash_id == None
- assert notebook.read_write == False
+ assert notebook.read_write == Notebook.READ_ONLY
assert notebook.owner == False
assert notebook.rank == None
@@ -317,7 +319,7 @@ class Test_users( Test_controller ):
assert notebook.revision
assert notebook.name == u"trash"
assert notebook.trash_id == None
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == None
@@ -326,7 +328,7 @@ class Test_users( Test_controller ):
assert notebook.revision
assert notebook.name == u"my notebook"
assert notebook.trash_id
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -335,7 +337,7 @@ class Test_users( Test_controller ):
assert notebook.revision == self.anon_notebook.revision
assert notebook.name == self.anon_notebook.name
assert notebook.trash_id == None
- assert notebook.read_write == False
+ assert notebook.read_write == Notebook.READ_ONLY
assert notebook.owner == False
assert notebook.rank == None
@@ -639,7 +641,7 @@ class Test_users( Test_controller ):
assert notebook.revision
assert notebook.name == u"trash"
assert notebook.trash_id == None
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == None
@@ -648,7 +650,7 @@ class Test_users( Test_controller ):
assert notebook.revision
assert notebook.name == u"my notebook"
assert notebook.trash_id
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -657,7 +659,7 @@ class Test_users( Test_controller ):
assert notebook.revision == self.anon_notebook.revision
assert notebook.name == self.anon_notebook.name
assert notebook.trash_id == None
- assert notebook.read_write == False
+ assert notebook.read_write == Notebook.READ_ONLY
assert notebook.owner == False
assert notebook.rank == None
@@ -740,27 +742,27 @@ class Test_users( Test_controller ):
assert len( result[ u"notebooks" ] ) == 5
assert result[ u"notebooks" ][ 0 ].object_id
assert result[ u"notebooks" ][ 0 ].name == u"trash"
- assert result[ u"notebooks" ][ 0 ].read_write == True
+ assert result[ u"notebooks" ][ 0 ].read_write == Notebook.READ_WRITE
assert result[ u"notebooks" ][ 0 ].owner == True
assert result[ u"notebooks" ][ 0 ].rank == None
assert result[ u"notebooks" ][ 1 ].object_id
assert result[ u"notebooks" ][ 1 ].name == u"trash"
- assert result[ u"notebooks" ][ 1 ].read_write == True
+ assert result[ u"notebooks" ][ 1 ].read_write == Notebook.READ_WRITE
assert result[ u"notebooks" ][ 1 ].owner == True
assert result[ u"notebooks" ][ 1 ].rank == None
assert result[ u"notebooks" ][ 2 ].object_id == self.notebooks[ 0 ].object_id
assert result[ u"notebooks" ][ 2 ].name == self.notebooks[ 0 ].name
- assert result[ u"notebooks" ][ 2 ].read_write == True
+ assert result[ u"notebooks" ][ 2 ].read_write == Notebook.READ_WRITE
assert result[ u"notebooks" ][ 2 ].owner == True
assert result[ u"notebooks" ][ 2 ].rank == 0
assert result[ u"notebooks" ][ 3 ].object_id == self.notebooks[ 1 ].object_id
assert result[ u"notebooks" ][ 3 ].name == self.notebooks[ 1 ].name
- assert result[ u"notebooks" ][ 3 ].read_write == True
+ assert result[ u"notebooks" ][ 3 ].read_write == Notebook.READ_WRITE
assert result[ u"notebooks" ][ 3 ].owner == True
assert result[ u"notebooks" ][ 3 ].rank == 1
assert result[ u"notebooks" ][ 4 ].object_id == self.anon_notebook.object_id
assert result[ u"notebooks" ][ 4 ].name == self.anon_notebook.name
- assert result[ u"notebooks" ][ 4 ].read_write == False
+ assert result[ u"notebooks" ][ 4 ].read_write == Notebook.READ_ONLY
assert result[ u"notebooks" ][ 4 ].owner == False
assert result[ u"notebooks" ][ 4 ].rank == None
assert result[ u"login_url" ] is None
@@ -783,7 +785,7 @@ class Test_users( Test_controller ):
assert len( result[ u"notebooks" ] ) == 1
assert result[ u"notebooks" ][ 0 ].object_id == self.anon_notebook.object_id
assert result[ u"notebooks" ][ 0 ].name == self.anon_notebook.name
- assert result[ u"notebooks" ][ 0 ].read_write == False
+ assert result[ u"notebooks" ][ 0 ].read_write == Notebook.READ_ONLY
assert result[ u"notebooks" ][ 0 ].owner == False
assert result[ u"notebooks" ][ 0 ].rank == None
@@ -831,8 +833,8 @@ class Test_users( Test_controller ):
invite_notebook_id = result[ u"redirect" ].split( u"/notebooks/" )[ -1 ]
assert invite_notebook_id == self.notebooks[ 0 ].object_id
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
def test_login_with_after_login( self ):
after_login = u"/foo/bar"
@@ -895,45 +897,215 @@ class Test_users( Test_controller ):
assert user.group_storage_bytes == 0
assert user.revision > previous_revision
- def test_check_access( self ):
- access = cherrypy.root.users.check_access( self.user.object_id, self.notebooks[ 0 ].object_id )
+ def test_load_notebook( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id )
- assert access is True
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
- def test_check_access_read_write( self ):
- access = cherrypy.root.users.check_access( self.user.object_id, self.notebooks[ 0 ].object_id, read_write = True )
+ def test_load_notebook_unknown_notebook( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, u"unknownid" )
- assert access is True
+ assert notebook is None
- def test_check_access_owner( self ):
- access = cherrypy.root.users.check_access( self.user.object_id, self.notebooks[ 0 ].object_id, owner = True )
+ def test_load_notebook_unknown_user( self ):
+ notebook = cherrypy.root.users.load_notebook( u"unknownuser", self.notebooks[ 0 ].object_id )
- assert access is True
+ assert notebook is None
- def test_check_access_full( self ):
- access = cherrypy.root.users.check_access( self.user.object_id, self.notebooks[ 0 ].object_id, read_write = True, owner = True )
+ def test_load_notebook_read_write( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id, read_write = True )
- assert access is True
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
- def test_check_access_anon( self ):
- access = cherrypy.root.users.check_access( self.user.object_id, self.anon_notebook.object_id )
+ def test_load_notebook_owner( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id, owner = True )
- assert access is True
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
- def test_check_access_anon_read_write( self ):
- access = cherrypy.root.users.check_access( self.user.object_id, self.anon_notebook.object_id, read_write = True )
+ def test_load_notebook_full( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id, read_write = True, owner = True )
- assert access is False
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
- def test_check_access_anon_owner( self ):
- access = cherrypy.root.users.check_access( self.user.object_id, self.anon_notebook.object_id, owner = True )
+ def test_load_notebook_with_note_id( self ):
+ note = Note.create(
+ self.database.next_id( Note ), u"hi
",
+ notebook_id = self.notebooks[ 0 ].object_id,
+ user_id = self.user.object_id,
+ )
+ self.database.save( note )
- assert access is False
+ self.database.execute( self.user.sql_update_access(
+ self.notebooks[ 0 ].object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = False,
+ ) )
- def test_check_access_anon_full( self ):
- access = cherrypy.root.users.check_access( self.user.object_id, self.anon_notebook.object_id, read_write = True, owner = True )
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = note.object_id )
- assert access is False
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
+
+ def test_load_notebook_with_note_id_by_another_user( self ):
+ note = Note.create(
+ self.database.next_id( Note ), u"hi from another user
",
+ notebook_id = self.notebooks[ 0 ].object_id,
+ user_id = self.user2.object_id,
+ )
+ self.database.save( note )
+
+ self.database.execute( self.user.sql_update_access(
+ self.notebooks[ 0 ].object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = False,
+ ) )
+
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = note.object_id )
+
+ assert notebook is None
+
+ def test_load_notebook_with_unknown_note_id( self ):
+ self.database.execute( self.user.sql_update_access(
+ self.notebooks[ 0 ].object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = False,
+ ) )
+
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = u"unknownid" )
+
+ assert notebook is None
+
+ def test_load_notebook_with_note_id_in_another_notebook( self ):
+ self.database.execute( self.user.sql_update_access(
+ self.notebooks[ 0 ].object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = False,
+ ) )
+
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = self.startup_note.object_id )
+
+ assert notebook is None
+
+ def test_load_notebook_read_write_with_note_id( self ):
+ note = Note.create(
+ self.database.next_id( Note ), u"hi
",
+ notebook_id = self.notebooks[ 0 ].object_id,
+ user_id = self.user.object_id,
+ )
+ self.database.save( note )
+
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = note.object_id )
+
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
+
+ def test_load_notebook_read_write_with_note_id_by_another_user( self ):
+ note = Note.create(
+ self.database.next_id( Note ), u"hi from another user
",
+ notebook_id = self.notebooks[ 0 ].object_id,
+ user_id = self.user2.object_id,
+ )
+ self.database.save( note )
+
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = note.object_id )
+
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
+
+ def test_load_notebook_read_write_with_unknown_note_id( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = u"unknownid" )
+
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
+
+ def test_load_notebook_read_write_with_note_id_in_another_notebook( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = self.startup_note.object_id )
+
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
+
+ def test_load_notebook_read_only_with_note_id( self ):
+ note = Note.create(
+ self.database.next_id( Note ), u"hi
",
+ notebook_id = self.notebooks[ 0 ].object_id,
+ user_id = self.user.object_id,
+ )
+ self.database.save( note )
+
+ self.database.execute( self.user.sql_update_access(
+ self.notebooks[ 0 ].object_id, read_write = Notebook.READ_ONLY, owner = False,
+ ) )
+
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = note.object_id )
+
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
+
+ def test_load_notebook_read_only_with_note_id_by_another_user( self ):
+ note = Note.create(
+ self.database.next_id( Note ), u"hi from another user
",
+ notebook_id = self.notebooks[ 0 ].object_id,
+ user_id = self.user2.object_id,
+ )
+ self.database.save( note )
+
+ self.database.execute( self.user.sql_update_access(
+ self.notebooks[ 0 ].object_id, read_write = Notebook.READ_ONLY, owner = False,
+ ) )
+
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = note.object_id )
+
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
+
+ def test_load_notebook_read_only_with_unknown_note_id( self ):
+ self.database.execute( self.user.sql_update_access(
+ self.notebooks[ 0 ].object_id, read_write = Notebook.READ_ONLY, owner = False,
+ ) )
+
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = u"unknownid" )
+
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
+
+ def test_load_notebook_read_only_with_note_id_in_another_notebook( self ):
+ self.database.execute( self.user.sql_update_access(
+ self.notebooks[ 0 ].object_id, read_write = Notebook.READ_ONLY, owner = False,
+ ) )
+
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
+ note_id = self.startup_note.object_id )
+
+ assert notebook
+ assert notebook.object_id == self.notebooks[ 0 ].object_id
+
+ def test_load_notebook_anon( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.anon_notebook.object_id )
+
+ assert notebook
+ assert notebook.object_id == self.anon_notebook.object_id
+
+ def test_load_notebook_anon_read_write( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.anon_notebook.object_id, read_write = True )
+
+ assert notebook is None
+
+ def test_load_notebook_anon_owner( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.anon_notebook.object_id, owner = True )
+
+ assert notebook is None
+
+ def test_load_notebook_anon_full( self ):
+ notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.anon_notebook.object_id, read_write = True, owner = True )
+
+ assert notebook is None
def test_check_group( self ):
membership = cherrypy.root.users.check_group( self.user.object_id, self.group.object_id )
@@ -1097,7 +1269,7 @@ class Test_users( Test_controller ):
assert len( result[ u"notebooks" ] ) == 1
assert result[ u"notebooks" ][ 0 ].object_id == self.anon_notebook.object_id
assert result[ u"notebooks" ][ 0 ].name == self.anon_notebook.name
- assert result[ u"notebooks" ][ 0 ].read_write == False
+ assert result[ u"notebooks" ][ 0 ].read_write == Notebook.READ_ONLY
assert result[ u"notebooks" ][ 0 ].owner == False
assert result[ u"notebooks" ][ 0 ].rank == None
@@ -2151,8 +2323,8 @@ class Test_users( Test_controller ):
invite_id = invite_id,
), session_id = self.session_id )
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
self.login()
result = self.http_post( "/users/revoke_invite", dict(
@@ -2163,8 +2335,8 @@ class Test_users( Test_controller ):
assert result[ u"message" ]
assert len( result[ u"invites" ] ) == 0
- assert not cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
- assert not cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
+ assert not cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
+ assert not cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
def test_revoke_invite_redeemed_self( self ):
self.login()
@@ -2191,8 +2363,8 @@ class Test_users( Test_controller ):
invite_id = invite_id,
), session_id = self.session_id )
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
# as user2, revoke that user's own invite
result = self.http_post( "/users/revoke_invite", dict(
@@ -2204,8 +2376,8 @@ class Test_users( Test_controller ):
assert len( result[ u"invites" ] ) == 0
# the user should no longer have any access
- assert not cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
- assert not cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
+ assert not cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
+ assert not cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
def test_revoke_invite_without_login( self ):
# login to send the invites, but don't send the logged-in session id for revoke_invite() below
@@ -2347,8 +2519,8 @@ class Test_users( Test_controller ):
), session_id = self.session_id )
# assert that access has been granted
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
# assert that the user is redirected to the notebook that the invite is for
assert result[ u"redirect"].startswith( u"/notebooks/" )
@@ -2385,8 +2557,8 @@ class Test_users( Test_controller ):
), session_id = self.session_id )
# assert that access is still granted
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
- assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
+ assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
# assert that the user is redirected to the notebook that the invite is for
assert result[ u"redirect"].startswith( u"/notebooks/" )
@@ -4104,7 +4276,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4145,7 +4317,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4184,7 +4356,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4224,7 +4396,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4262,7 +4434,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4307,7 +4479,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4436,7 +4608,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4484,7 +4656,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4532,7 +4704,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4578,7 +4750,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4627,7 +4799,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
@@ -4675,7 +4847,7 @@ class Test_users( Test_controller ):
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
assert notebook.object_id == self.notebooks[ 0 ].object_id
assert notebook.name == self.notebooks[ 0 ].name
- assert notebook.read_write == True
+ assert notebook.read_write == Notebook.READ_WRITE
assert notebook.owner == True
assert notebook.rank == 0
diff --git a/luminotes.py b/luminotes.py
index e72d52c..d7e7022 100755
--- a/luminotes.py
+++ b/luminotes.py
@@ -9,8 +9,10 @@ import urllib2 as urllib
import cherrypy
import webbrowser
from controller.Database import Database
+from controller.Schema_upgrader import Schema_upgrader
from controller.Root import Root
from config import Common
+from config.Version import VERSION
INITIAL_SOCKET_TIMEOUT_SECONDS = 1
@@ -103,6 +105,10 @@ def main( args ):
ssl_mode = cherrypy.config.configMap[ u"global" ].get( u"luminotes.db_ssl_mode" ),
)
+ # if necessary, upgrade the database schema to match this current version of the code
+ schema_upgrader = Schema_upgrader( database )
+ schema_upgrader.upgrade_schema( to_version = VERSION )
+
cherrypy.lowercase_api = True
root = Root( database, cherrypy.config.configMap )
cherrypy.root = root
@@ -114,6 +120,7 @@ def callback( log_access_file, log_file, server_url, port_filename, socket_port,
# record our listening socket port
if port_filename:
port_file = file( port_filename, "w" )
+ os.chmod( port_filename, stat.S_IRUSR | stat.S_IWUSR )
port_file.write( "%s" % socket_port )
port_file.close()
diff --git a/model/Notebook.py b/model/Notebook.py
index f74b344..da290fe 100644
--- a/model/Notebook.py
+++ b/model/Notebook.py
@@ -12,8 +12,12 @@ class Notebook( Persistent ):
WHITESPACE_PATTERN = re.compile( r"\s+" )
SEARCH_OPERATORS = re.compile( r"[&|!()'\\:]" )
+ READ_ONLY = 0 # user can only view the notes within this notebook
+ READ_WRITE = 1 # user can view and edit the notes within this notebook
+ READ_WRITE_FOR_OWN_NOTES = 2 # user can only edit their own notes, not notes created by others
+
def __init__( self, object_id, revision = None, name = None, trash_id = None, deleted = False,
- user_id = None, read_write = True, owner = True, rank = None ):
+ user_id = None, read_write = None, owner = True, rank = None, own_notes_only = False ):
"""
Create a new notebook with the given id and name.
@@ -30,11 +34,14 @@ class Notebook( Persistent ):
@type user_id: unicode or NoneType
@param user_id: id of the user who most recently updated this notebook object (optional)
@type read_write: bool or NoneType
- @param read_write: whether this view of the notebook is currently read-write (optional, defaults to True)
+ @param read_write: whether this view of the notebook is currently read-write. one of:
+ READ_ONLY, READ_WRITE, READ_WRITE_FOR_OWN_NOTES (optional, defaults to READ_WRITE)
@type owner: bool or NoneType
@param owner: whether this view of the notebook currently has owner-level access (optional, defaults to True)
@type rank: float or NoneType
@param rank: indicates numeric ordering of this note in relation to other notebooks
+ @type own_notes_only: bool or NoneType
+ @param own_notes_only: True makes read_write be READ_WRITE_FOR_OWN_NOTES (optional, defaults to False)
@rtype: Notebook
@return: newly constructed notebook
"""
@@ -43,12 +50,22 @@ class Notebook( Persistent ):
self.__trash_id = trash_id
self.__deleted = deleted
self.__user_id = user_id
+
+ read_write = {
+ None: Notebook.READ_WRITE,
+ True: Notebook.READ_WRITE,
+ False: Notebook.READ_ONLY,
+ }.get( read_write, read_write )
+
+ if own_notes_only is True and read_write != Notebook.READ_ONLY:
+ read_write = Notebook.READ_WRITE_FOR_OWN_NOTES
+
self.__read_write = read_write
self.__owner = owner
self.__rank = rank
@staticmethod
- def create( object_id, name = None, trash_id = None, deleted = False, user_id = None, read_write = True, owner = True, rank = None ):
+ def create( object_id, name = None, trash_id = None, deleted = False, user_id = None, read_write = None, owner = True, rank = None, own_notes_only = False ):
"""
Convenience constructor for creating a new notebook.
@@ -63,15 +80,18 @@ class Notebook( Persistent ):
@type user_id: unicode or NoneType
@param user_id: id of the user who most recently updated this notebook object (optional)
@type read_write: bool or NoneType
- @param read_write: whether this view of the notebook is currently read-write (optional, defaults to True)
+ @param read_write: whether this view of the notebook is currently read-write. one of:
+ READ_ONLY, READ_WRITE, READ_WRITE_FOR_OWN_NOTES (optional, defaults to READ_WRITE)
@type owner: bool or NoneType
@param owner: whether this view of the notebook currently has owner-level access (optional, defaults to True)
@type rank: float or NoneType
@param rank: indicates numeric ordering of this note in relation to other notebooks
+ @type own_notes_only: bool or NoneType
+ @param own_notes_only: True makes read_write be READ_WRITE_FOR_OWN_NOTES (optional, defaults to False)
@rtype: Notebook
@return: newly constructed notebook
"""
- return Notebook( object_id, name = name, trash_id = trash_id, user_id = user_id, read_write = read_write, owner = owner, rank = rank )
+ return Notebook( object_id, name = name, trash_id = trash_id, user_id = user_id, read_write = read_write, owner = owner, rank = rank, own_notes_only = own_notes_only )
@staticmethod
def sql_load( object_id, revision = None ):
@@ -264,6 +284,42 @@ class Notebook( Persistent ):
"select count( id ) from note_current where notebook_id = %s;" % \
( quote( self.object_id ) )
+ def sql_load_tag_by_name( self, user_id, tag_name ):
+ """
+ Return a SQL string to load a tag associated with this notebook by the given user.
+ """
+ return \
+ """
+ select
+ tag.id, tag.revision, tag.notebook_id, tag.user_id, tag.name, tag.description, tag_notebook.value
+ from
+ tag_notebook, tag
+ where
+ tag_notebook.notebook_id = %s and
+ tag_notebook.user_id = %s and
+ tag_notebook.tag_id = tag.id and
+ tag.name = %s
+ order by tag.name;
+ """ % ( quote( self.object_id ), quote( user_id ), quote( tag_name ) )
+
+ def sql_load_tags( self, user_id ):
+ """
+ Return a SQL string to load a list of all the tags associated with this notebook by the given
+ user.
+ """
+ return \
+ """
+ select
+ tag.id, tag.revision, tag.notebook_id, tag.user_id, tag.name, tag.description, tag_notebook.value
+ from
+ tag_notebook, tag
+ where
+ tag_notebook.notebook_id = %s and
+ tag_notebook.user_id = %s and
+ tag_notebook.tag_id = tag.id
+ order by tag.name;
+ """ % ( quote( self.object_id ), quote( user_id ) )
+
def to_dict( self ):
d = Persistent.to_dict( self )
@@ -285,6 +341,12 @@ class Notebook( Persistent ):
def __set_read_write( self, read_write ):
# The read_write member isn't actually saved to the database, so setting it doesn't need to
# call update_revision().
+ read_write = {
+ None: Notebook.READ_WRITE,
+ True: Notebook.READ_WRITE,
+ False: Notebook.READ_ONLY,
+ }.get( read_write, read_write )
+
self.__read_write = read_write
def __set_owner( self, owner ):
diff --git a/model/Tag.py b/model/Tag.py
new file mode 100644
index 0000000..8ba77dd
--- /dev/null
+++ b/model/Tag.py
@@ -0,0 +1,135 @@
+from Persistent import Persistent, quote
+
+
+class Tag( Persistent ):
+ """
+ A tag for a note or a notebook.
+ """
+ def __init__( self, object_id, revision = None, notebook_id = None, user_id = None, name = None, description = None, value = None ):
+ """
+ Create a Tag with the given id.
+
+ @type object_id: unicode
+ @param object_id: id of the Tag
+ @type revision: datetime or NoneType
+ @param revision: revision timestamp of the object (optional, defaults to now)
+ @type notebook_id: unicode or NoneType
+ @param notebook_id: id of the notebook whose namespace this tag is in, if any
+ @type user_id: unicode or NoneType
+ @param user_id: id of the user who most recently updated this tag, if any
+ @type name: unicode or NoneType
+ @param name: name of the tag (optional)
+ @type description: unicode or NoneType
+ @param description: brief description of the tag (optional)
+ @type value: unicode or NoneType
+ @param value: per-note or per-notebook value of the tag (optional)
+ @rtype: Tag
+ @return: newly constructed Tag
+ """
+ Persistent.__init__( self, object_id, revision )
+ self.__notebook_id = notebook_id
+ self.__user_id = user_id
+ self.__name = name
+ self.__description = description
+ self.__value = value
+
+ @staticmethod
+ def create( object_id, notebook_id = None, user_id = None, name = None, description = None, value = None ):
+ """
+ Convenience constructor for creating a new Tag.
+
+ @type object_id: unicode
+ @param object_id: id of the Tag
+ @type notebook_id: unicode or NoneType
+ @param notebook_id: id of the notebook whose namespace this tag is in, if any
+ @type user_id: unicode or NoneType
+ @param user_id: id of the user who most recently updated this tag, if any
+ @type name: unicode or NoneType
+ @param name: name of the tag (optional)
+ @type description: unicode or NoneType
+ @param description: brief description of the tag (optional)
+ @type value: unicode or NoneType
+ @param value: per-note or per-notebook value of the tag (optional)
+ @rtype: Tag
+ @return: newly constructed Tag
+ """
+ return Tag( object_id, notebook_id = notebook_id, user_id = user_id, name = name, description = description, value = value )
+
+ @staticmethod
+ def sql_load( object_id, revision = None ):
+ # Tags don't store old revisions
+ if revision:
+ raise NotImplementedError()
+
+ return \
+ """
+ select
+ tag.id, tag.revision, tag.notebook_id, tag.user_id, tag.name, tag.description
+ from
+ tag
+ where
+ tag.id = %s;
+ """ % quote( object_id )
+
+ @staticmethod
+ def sql_load_by_name( name, notebook_id = None, user_id = None ):
+ if notebook_id:
+ notebook_id_clause = " and tag.notebook_id = %s" % quote( notebook_id )
+ else:
+ notebook_id_clause = ""
+
+ if user_id:
+ user_id_clause = " and tag.user_id = %s" % quote( user_id )
+ else:
+ user_id_clause = ""
+
+ return \
+ """
+ select
+ tag.id, tag.revision, tag.notebook_id, tag.user_id, tag.name, tag.description
+ from
+ tag
+ where
+ tag.name = %s%s%s;
+ """ % ( quote( name ), notebook_id_clause, user_id_clause )
+
+ @staticmethod
+ def sql_id_exists( object_id, revision = None ):
+ if revision:
+ raise NotImplementedError()
+
+ return "select id from tag where id = %s;" % quote( object_id )
+
+ def sql_exists( self ):
+ return Tag.sql_id_exists( self.object_id )
+
+ def sql_create( self ):
+ return "insert into tag ( id, revision, notebook_id, user_id, name, description ) values ( %s, %s, %s, %s, %s, %s );" % \
+ ( quote( self.object_id ), quote( self.revision ), quote( self.__notebook_id ),
+ quote( self.__user_id ), quote( self.__name ), quote( self.__description ) )
+
+ def sql_update( self ):
+ return "update tag set revision = %s, notebook_id = %s, user_id = %s, name = %s, description = %s where id = %s;" % \
+ ( quote( self.revision ), quote( self.__notebook_id ), quote( self.__user_id ),
+ quote( self.__name ), quote( self.__description ), quote( self.object_id ) )
+
+ def sql_delete( self ):
+ return "delete from tag where id = %s;" % quote( self.object_id )
+
+ def to_dict( self ):
+ d = Persistent.to_dict( self )
+ d.update( dict(
+ notebook_id = self.__notebook_id,
+ user_id = self.__user_id,
+ name = self.__name,
+ description = self.__description,
+ value = self.__value,
+ ) )
+
+ return d
+
+ notebook_id = property( lambda self: self.__notebook_id )
+ user_id = property( lambda self: self.__user_id )
+ name = property( lambda self: self.__name )
+ description = property( lambda self: self.__description )
+ value = property( lambda self: self.__value )
diff --git a/model/User.py b/model/User.py
index 80a19f3..97d2c59 100644
--- a/model/User.py
+++ b/model/User.py
@@ -2,6 +2,7 @@ import sha
import random
from copy import copy
from Persistent import Persistent, quote
+from Notebook import Notebook
class User( Persistent ):
@@ -132,7 +133,8 @@ class User( Persistent ):
def sql_load_by_email_address( email_address ):
return "select * from luminotes_user_current where email_address = %s;" % quote( email_address )
- def sql_load_notebooks( self, parents_only = False, undeleted_only = False, read_write = False ):
+ def sql_load_notebooks( self, parents_only = False, undeleted_only = False, read_write = False,
+ tag_name = None, tag_value = None, notebook_id = None ):
"""
Return a SQL string to load a list of the notebooks to which this user has access.
"""
@@ -151,28 +153,55 @@ class User( Persistent ):
else:
read_write_clause = ""
+ if tag_name:
+ tag_tables = ", tag_notebook, tag"
+ tag_clause = \
+ """
+ and tag_notebook.tag_id = tag.id and tag_notebook.user_id = %s and
+ tag_notebook.notebook_id = notebook_current.id and tag.name = %s
+ """ % ( quote( self.object_id ), quote( tag_name ) )
+
+ if tag_value:
+ tag_clause += " and tag_notebook.value = %s" % quote( tag_value )
+ else:
+ tag_tables = ""
+ tag_clause = ""
+
+ # useful for loading just a single notebook that the user has access to
+ if notebook_id:
+ notebook_id_clause = " and notebook_current.id = %s" % quote( notebook_id )
+ else:
+ notebook_id_clause = ""
+
return \
"""
select
- notebook_current.*, user_notebook.read_write, user_notebook.owner, user_notebook.rank
+ notebook_current.*, user_notebook.read_write, user_notebook.owner, user_notebook.rank, user_notebook.own_notes_only
from
- user_notebook, notebook_current
+ user_notebook, notebook_current%s
where
- user_notebook.user_id = %s%s%s%s and
+ user_notebook.user_id = %s%s%s%s%s%s and
user_notebook.notebook_id = notebook_current.id
order by user_notebook.rank;
- """ % ( quote( self.object_id ), parents_only_clause, undeleted_only_clause, read_write_clause )
+ """ % ( tag_tables, quote( self.object_id ), parents_only_clause, undeleted_only_clause,
+ read_write_clause, tag_clause, notebook_id_clause )
- def sql_save_notebook( self, notebook_id, read_write = True, owner = True, rank = None ):
+ def sql_save_notebook( self, notebook_id, read_write = True, owner = True, rank = None, own_notes_only = False ):
"""
Return a SQL string to save the id of a notebook to which this user has access.
"""
if rank is None: rank = quote( None )
return \
- "insert into user_notebook ( user_id, notebook_id, read_write, owner, rank ) values " + \
- "( %s, %s, %s, %s, %s );" % ( quote( self.object_id ), quote( notebook_id ), quote( read_write and 't' or 'f' ),
- quote( owner and 't' or 'f' ), rank )
+ "insert into user_notebook ( user_id, notebook_id, read_write, owner, rank, own_notes_only ) values " + \
+ "( %s, %s, %s, %s, %s, %s );" % (
+ quote( self.object_id ),
+ quote( notebook_id ),
+ quote( read_write and 't' or 'f' ),
+ quote( owner and 't' or 'f' ),
+ rank,
+ quote( own_notes_only and 't' or 'f' ),
+ )
def sql_remove_notebook( self, notebook_id ):
"""
@@ -202,14 +231,26 @@ class User( Persistent ):
"select user_id from user_notebook where user_id = %s and notebook_id = %s;" % \
( quote( self.object_id ), quote( notebook_id ) )
- def sql_update_access( self, notebook_id, read_write = False, owner = False ):
+ def sql_update_access( self, notebook_id, read_write = Notebook.READ_ONLY, owner = False ):
"""
Return a SQL string to update the user's notebook access to the given read_write and owner level.
"""
return \
- "update user_notebook set read_write = %s, owner = %s where user_id = %s and notebook_id = %s;" % \
- ( quote( read_write and 't' or 'f' ), quote( owner and 't' or 'f' ), quote( self.object_id ),
- quote( notebook_id ) )
+ "update user_notebook set read_write = %s, owner = %s, own_notes_only = %s where user_id = %s and notebook_id = %s;" % (
+ quote( ( read_write != Notebook.READ_ONLY ) and 't' or 'f' ),
+ quote( owner and 't' or 'f' ),
+ quote( ( read_write == Notebook.READ_WRITE_FOR_OWN_NOTES ) and 't' or 'f' ),
+ quote( self.object_id ),
+ quote( notebook_id ),
+ )
+
+ def sql_save_notebook_tag( self, notebook_id, tag_id, value = None ):
+ """
+ Return a SQL string to associate a tag with a notebook of this user.
+ """
+ return \
+ "insert into tag_notebook ( notebook_id, tag_id, value, user_id ) values " + \
+ "( %s, %s, %s, %s );" % ( quote( notebook_id ), quote( tag_id ), quote( value ), quote( self.object_id ) )
def sql_update_notebook_rank( self, notebook_id, rank ):
"""
diff --git a/model/delta/1.5.5.sql b/model/delta/1.5.5.sql
new file mode 100644
index 0000000..b39b9bb
--- /dev/null
+++ b/model/delta/1.5.5.sql
@@ -0,0 +1,30 @@
+create table tag (
+ id text,
+ revision timestamp with time zone,
+ notebook_id text,
+ user_id text,
+ name text,
+ description text
+);
+ALTER TABLE ONLY tag ADD CONSTRAINT tag_pkey PRIMARY KEY (id);
+CREATE INDEX tag_notebook_id_index ON tag USING btree (notebook_id);
+CREATE INDEX tag_user_id_index ON tag USING btree (user_id);
+
+create table tag_notebook (
+ notebook_id text,
+ tag_id text,
+ value text,
+ user_id text
+);
+ALTER TABLE ONLY tag_notebook ADD CONSTRAINT tag_notebook_pkey PRIMARY KEY (user_id, notebook_id, tag_id);
+
+create table tag_note (
+ note_id text,
+ tag_id text,
+ value text
+);
+ALTER TABLE ONLY tag_note ADD CONSTRAINT tag_note_pkey PRIMARY KEY (note_id, tag_id);
+
+ALTER TABLE user_notebook ADD COLUMN own_notes_only boolean DEFAULT false;
+
+update user_notebook set rank = 0 from luminotes_user_current, notebook_current where user_notebook.user_id = luminotes_user_current.id and username = 'anonymous' and user_notebook.notebook_id = notebook_current.id and notebook_current.name = 'Luminotes';
diff --git a/model/delta/1.5.5.sqlite b/model/delta/1.5.5.sqlite
new file mode 100644
index 0000000..22091af
--- /dev/null
+++ b/model/delta/1.5.5.sqlite
@@ -0,0 +1,37 @@
+create table tag (
+ id text,
+ revision timestamp with time zone,
+ notebook_id text,
+ user_id text,
+ name text,
+ description text
+);
+CREATE INDEX tag_pkey ON tag (id);
+CREATE INDEX tag_notebook_id_index ON tag (notebook_id);
+CREATE INDEX tag_user_id_index ON tag (user_id);
+
+create table tag_notebook (
+ notebook_id text,
+ tag_id text,
+ value text,
+ user_id text
+);
+CREATE INDEX tag_notebook_pkey ON tag_notebook (user_id, notebook_id, tag_id);
+
+create table tag_note (
+ note_id text,
+ tag_id text,
+ value text
+);
+CREATE INDEX tag_note_pkey ON tag_note (note_id, tag_id);
+
+CREATE INDEX file_pkey ON file (id);
+CREATE INDEX invite_pkey ON invite (id);
+CREATE INDEX luminotes_user_pkey ON luminotes_user (id, revision);
+CREATE INDEX note_pkey ON note (id, revision);
+CREATE INDEX notebook_pkey ON notebook (id, revision);
+CREATE INDEX password_reset_pkey ON password_reset (id);
+CREATE INDEX download_access_pkey ON download_access (id);
+CREATE INDEX user_notebook_pkey ON user_notebook (user_id, notebook_id);
+
+ALTER TABLE user_notebook ADD COLUMN own_notes_only boolean DEFAULT false;
diff --git a/model/drop.sql b/model/drop.sql
index c392d37..f6cff28 100644
--- a/model/drop.sql
+++ b/model/drop.sql
@@ -12,4 +12,7 @@ DROP TABLE user_notebook;
DROP TABLE user_group;
DROP TABLE invite;
DROP TABLE file;
+DROP TABLE tag;
+DROP TABLE tag_notebook;
+DROP TABLE tag_note;
DROP FUNCTION drop_html_tags( text );
diff --git a/model/schema.sql b/model/schema.sql
index 716f760..1acbb05 100644
--- a/model/schema.sql
+++ b/model/schema.sql
@@ -1,5 +1,5 @@
--
--- PostgreSQL database dump
+-- PostgreSQL database schema
--
SET client_encoding = 'UTF8';
@@ -12,22 +12,10 @@ SET default_tablespace = '';
SET default_with_oids = false;
-
---
--- Name: drop_html_tags(text); Type: FUNCTION; Schema: public; Owner: luminotes
---
-
CREATE FUNCTION drop_html_tags(text) RETURNS text
AS $_$select regexp_replace( regexp_replace( $1, '?(div|p|br|ul|ol|li|h3)( [^>]*?)?/?>', ' ', 'gi' ), '<[^>]+?>', '', 'g' );$_$
LANGUAGE sql;
-
-
ALTER FUNCTION public.drop_html_tags(text) OWNER TO luminotes;
-
---
--- Name: file; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE file (
id text NOT NULL,
revision timestamp with time zone,
@@ -37,14 +25,30 @@ CREATE TABLE file (
size_bytes integer,
content_type text
);
-
-
ALTER TABLE public.file OWNER TO luminotes;
+CREATE TABLE tag (
+ id text NOT NULL,
+ revision timestamp with time zone,
+ notebook_id text,
+ user_id text,
+ name text,
+ description text
+);
+ALTER TABLE public.tag OWNER TO luminotes;
+CREATE TABLE tag_notebook (
+ notebook_id text,
+ tag_id text,
+ value text,
+ user_id text
+);
---
--- Name: invite; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
+ALTER TABLE public.tag_notebook OWNER TO luminotes;
+CREATE TABLE tag_note (
+ note_id text,
+ tag_id text,
+ value text
+);
+ALTER TABLE public.tag_note OWNER TO luminotes;
CREATE TABLE invite (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
@@ -55,37 +59,16 @@ CREATE TABLE invite (
"owner" boolean,
redeemed_user_id text
);
-
-
ALTER TABLE public.invite OWNER TO luminotes;
-
---
--- Name: luminotes_group; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE luminotes_group (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
name text
);
-
-
ALTER TABLE public.luminotes_group OWNER TO luminotes;
-
---
--- Name: luminotes_group_current; Type: VIEW; Schema: public; Owner: luminotes
---
-
CREATE VIEW luminotes_group_current AS
SELECT luminotes_group.id, luminotes_group.revision, luminotes_group.name FROM luminotes_group WHERE (luminotes_group.revision IN (SELECT max(sub_group.revision) AS max FROM luminotes_group sub_group WHERE (sub_group.id = luminotes_group.id)));
-
-
ALTER TABLE public.luminotes_group_current OWNER TO luminotes;
-
---
--- Name: luminotes_user; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE luminotes_user (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
@@ -96,24 +79,10 @@ CREATE TABLE luminotes_user (
storage_bytes integer,
rate_plan integer
);
-
-
ALTER TABLE public.luminotes_user OWNER TO luminotes;
-
---
--- Name: luminotes_user_current; Type: VIEW; Schema: public; Owner: luminotes
---
-
CREATE VIEW luminotes_user_current AS
SELECT luminotes_user.id, luminotes_user.revision, luminotes_user.username, luminotes_user.salt, luminotes_user.password_hash, luminotes_user.email_address, luminotes_user.storage_bytes, luminotes_user.rate_plan FROM luminotes_user WHERE (luminotes_user.revision IN (SELECT max(sub_user.revision) AS max FROM luminotes_user sub_user WHERE (sub_user.id = luminotes_user.id)));
-
-
ALTER TABLE public.luminotes_user_current OWNER TO luminotes;
-
---
--- Name: note; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE note (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
@@ -126,24 +95,10 @@ CREATE TABLE note (
search tsvector,
user_id text
);
-
-
ALTER TABLE public.note OWNER TO luminotes;
-
---
--- Name: note_current; Type: VIEW; Schema: public; Owner: luminotes
---
-
CREATE VIEW note_current AS
SELECT note.id, note.revision, note.title, note.contents, note.notebook_id, note.startup, note.deleted_from_id, note.rank, note.search, note.user_id FROM note WHERE (note.revision IN (SELECT max(sub_note.revision) AS max FROM note sub_note WHERE (sub_note.id = note.id)));
-
-
ALTER TABLE public.note_current OWNER TO luminotes;
-
---
--- Name: notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE notebook (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
@@ -152,239 +107,106 @@ CREATE TABLE notebook (
deleted boolean DEFAULT false,
user_id text
);
-
-
ALTER TABLE public.notebook OWNER TO luminotes;
-
---
--- Name: notebook_current; Type: VIEW; Schema: public; Owner: luminotes
---
-
CREATE VIEW notebook_current AS
SELECT notebook.id, notebook.revision, notebook.name, notebook.trash_id, notebook.deleted, notebook.user_id FROM notebook WHERE (notebook.revision IN (SELECT max(sub_notebook.revision) AS max FROM notebook sub_notebook WHERE (sub_notebook.id = notebook.id)));
-
-
ALTER TABLE public.notebook_current OWNER TO luminotes;
-
---
--- Name: password_reset; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE password_reset (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
email_address text,
redeemed boolean
);
-
-
ALTER TABLE public.password_reset OWNER TO luminotes;
-
--- Name: download_access; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE download_access (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
item_number text,
transaction_id text
);
-
-
ALTER TABLE public.download_access OWNER TO luminotes;
-
---
---
--- Name: user_group; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE user_group (
user_id text NOT NULL,
group_id text NOT NULL,
"admin" boolean DEFAULT false
);
-
-
ALTER TABLE public.user_group OWNER TO luminotes;
-
---
--- Name: user_notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE user_notebook (
user_id text NOT NULL,
notebook_id text NOT NULL,
read_write boolean DEFAULT false,
"owner" boolean DEFAULT false,
- rank numeric
+ rank numeric,
+ own_notes_only boolean DEFAULT false
);
-
-
ALTER TABLE public.user_notebook OWNER TO luminotes;
-
--- Name: file_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
---
-
ALTER TABLE ONLY file
ADD CONSTRAINT file_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY tag
+ ADD CONSTRAINT tag_pkey PRIMARY KEY (id);
---
--- Name: invite_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
---
+ALTER TABLE ONLY tag_notebook
+ ADD CONSTRAINT tag_notebook_pkey PRIMARY KEY (user_id, notebook_id, tag_id);
+
+ALTER TABLE ONLY tag_note
+ ADD CONSTRAINT tag_note_pkey PRIMARY KEY (note_id, tag_id);
ALTER TABLE ONLY invite
ADD CONSTRAINT invite_pkey PRIMARY KEY (id);
-
---
--- Name: luminotes_user_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
---
-
ALTER TABLE ONLY luminotes_user
ADD CONSTRAINT luminotes_user_pkey PRIMARY KEY (id, revision);
-
---
--- Name: note_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
---
-
ALTER TABLE ONLY note
ADD CONSTRAINT note_pkey PRIMARY KEY (id, revision);
-
---
--- Name: notebook_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
---
-
ALTER TABLE ONLY notebook
ADD CONSTRAINT notebook_pkey PRIMARY KEY (id, revision);
-
---
--- Name: password_reset_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
---
-
ALTER TABLE ONLY password_reset
ADD CONSTRAINT password_reset_pkey PRIMARY KEY (id);
-
---
--- Name: download_access_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
---
-
ALTER TABLE ONLY download_access
ADD CONSTRAINT download_access_pkey PRIMARY KEY (id);
-
---
--- Name: user_notebook_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
---
-
ALTER TABLE ONLY user_notebook
ADD CONSTRAINT user_notebook_pkey PRIMARY KEY (user_id, notebook_id);
-
---
--- Name: file_note_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX file_note_id_index ON file USING btree (note_id);
-
---
--- Name: file_notebook_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX file_notebook_id_index ON file USING btree (notebook_id);
+CREATE INDEX tag_notebook_id_index ON tag USING btree (notebook_id);
---
--- Name: luminotes_group_pkey; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
+CREATE INDEX tag_user_id_index ON tag USING btree (user_id);
CREATE INDEX luminotes_group_pkey ON luminotes_group USING btree (id, revision);
-
---
--- Name: luminotes_user_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX luminotes_user_email_address_index ON luminotes_user USING btree (email_address);
-
---
--- Name: luminotes_user_username_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX luminotes_user_username_index ON luminotes_user USING btree (username);
-
---
--- Name: note_notebook_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX note_notebook_id_index ON note USING btree (notebook_id);
-
---
--- Name: note_notebook_id_startup_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX note_notebook_id_startup_index ON note USING btree (notebook_id, startup);
-
---
--- Name: note_notebook_id_title_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX note_notebook_id_title_index ON note USING btree (notebook_id, md5(title));
-
---
--- Name: password_reset_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX password_reset_email_address_index ON password_reset USING btree (email_address);
-
---
--- Name: download_access_transaction_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX download_access_transaction_id_index ON download_access USING btree (transaction_id);
-
---
--- Name: search_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX search_index ON note USING gist (search);
-
---
--- Name: search_update; Type: TRIGGER; Schema: public; Owner: luminotes
---
-
CREATE TRIGGER search_update
BEFORE INSERT OR UPDATE ON note
FOR EACH ROW
EXECUTE PROCEDURE tsearch2('search', 'drop_html_tags', 'title', 'contents');
-
---
--- Name: public; Type: ACL; Schema: -; Owner: postgres
---
-
REVOKE ALL ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON SCHEMA public FROM postgres;
GRANT ALL ON SCHEMA public TO postgres;
GRANT ALL ON SCHEMA public TO PUBLIC;
-
-
---
--- PostgreSQL database dump complete
---
-
diff --git a/model/schema.sqlite b/model/schema.sqlite
index a8563eb..707f0cd 100644
--- a/model/schema.sqlite
+++ b/model/schema.sqlite
@@ -1,5 +1,5 @@
--
--- Name: file; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
+-- SQLite database schema
--
CREATE TABLE file (
@@ -12,10 +12,27 @@ CREATE TABLE file (
content_type text
);
+CREATE TABLE tag (
+ id text NOT NULL,
+ revision timestamp with time zone,
+ notebook_id text,
+ user_id text,
+ name text,
+ description text
+);
---
--- Name: invite; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
+CREATE TABLE tag_notebook (
+ notebook_id text,
+ tag_id text,
+ value text,
+ user_id text
+);
+
+CREATE TABLE tag_note (
+ note_id text,
+ tag_id text,
+ value text
+);
CREATE TABLE invite (
id text NOT NULL,
@@ -28,30 +45,15 @@ CREATE TABLE invite (
redeemed_user_id text
);
-
---
--- Name: luminotes_group; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE luminotes_group (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
name text
);
-
---
--- Name: luminotes_group_current; Type: VIEW; Schema: public; Owner: luminotes
---
-
CREATE VIEW luminotes_group_current AS
SELECT id, revision, name FROM luminotes_group WHERE (luminotes_group.revision IN (SELECT max(sub_group.revision) AS max FROM luminotes_group sub_group WHERE (sub_group.id = luminotes_group.id)));
-
---
--- Name: luminotes_user; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE luminotes_user (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
@@ -63,19 +65,9 @@ CREATE TABLE luminotes_user (
rate_plan integer
);
-
---
--- Name: luminotes_user_current; Type: VIEW; Schema: public; Owner: luminotes
---
-
CREATE VIEW luminotes_user_current AS
SELECT id, revision, username, salt, password_hash, email_address, storage_bytes, rate_plan FROM luminotes_user WHERE (luminotes_user.revision IN (SELECT max(sub_user.revision) AS max FROM luminotes_user sub_user WHERE (sub_user.id = luminotes_user.id)));
-
---
--- Name: note; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE note (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
@@ -89,19 +81,9 @@ CREATE TABLE note (
user_id text
);
-
---
--- Name: note_current; Type: VIEW; Schema: public; Owner: luminotes
---
-
CREATE VIEW note_current AS
SELECT id, revision, title, contents, notebook_id, startup, deleted_from_id, rank, search, user_id FROM note WHERE (note.revision IN (SELECT max(sub_note.revision) AS max FROM note sub_note WHERE (sub_note.id = note.id)));
-
---
--- Name: notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE notebook (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
@@ -111,19 +93,9 @@ CREATE TABLE notebook (
user_id text
);
-
---
--- Name: notebook_current; Type: VIEW; Schema: public; Owner: luminotes
---
-
CREATE VIEW notebook_current AS
SELECT id, revision, name, trash_id, deleted, user_id FROM notebook WHERE (notebook.revision IN (SELECT max(sub_notebook.revision) AS max FROM notebook sub_notebook WHERE (sub_notebook.id = notebook.id)));
-
---
--- Name: password_reset; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE password_reset (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
@@ -131,10 +103,6 @@ CREATE TABLE password_reset (
redeemed boolean
);
-
--- Name: download_access; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE download_access (
id text NOT NULL,
revision timestamp with time zone NOT NULL,
@@ -142,118 +110,69 @@ CREATE TABLE download_access (
transaction_id text
);
-
---
--- Name: user_group; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE user_group (
user_id text NOT NULL,
group_id text NOT NULL,
"admin" boolean DEFAULT false
);
-
---
--- Name: user_notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE TABLE user_notebook (
user_id text NOT NULL,
notebook_id text NOT NULL,
read_write boolean DEFAULT false,
"owner" boolean DEFAULT false,
- rank numeric
+ rank numeric,
+ own_notes_only boolean DEFAULT false
);
+CREATE INDEX file_pkey ON file (id);
+CREATE INDEX tag_pkey ON tag (id);
---
--- Name: file_note_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
+CREATE INDEX tag_notebook_pkey ON tag_notebook (user_id, notebook_id, tag_id);
+
+CREATE INDEX tag_note_pkey ON tag_note (note_id, tag_id);
+
+CREATE INDEX invite_pkey ON invite (id);
+
+CREATE INDEX luminotes_user_pkey ON luminotes_user (id, revision);
+
+CREATE INDEX note_pkey ON note (id, revision);
+
+CREATE INDEX notebook_pkey ON notebook (id, revision);
+
+CREATE INDEX password_reset_pkey ON password_reset (id);
+
+CREATE INDEX download_access_pkey ON download_access (id);
+
+CREATE INDEX user_notebook_pkey ON user_notebook (user_id, notebook_id);
CREATE INDEX file_note_id_index ON file (note_id);
-
---
--- Name: file_notebook_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX file_notebook_id_index ON file (notebook_id);
+CREATE INDEX tag_notebook_id_index ON tag (notebook_id);
---
--- Name: luminotes_group_pkey; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
+CREATE INDEX tag_user_id_index ON tag (user_id);
CREATE INDEX luminotes_group_pkey ON luminotes_group (id, revision);
-
---
--- Name: luminotes_user_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX luminotes_user_email_address_index ON luminotes_user (email_address);
-
---
--- Name: luminotes_user_username_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX luminotes_user_username_index ON luminotes_user (username);
-
---
--- Name: note_notebook_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX note_notebook_id_index ON note (notebook_id);
-
---
--- Name: note_notebook_id_startup_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX note_notebook_id_startup_index ON note (notebook_id, startup);
-
---
--- Name: note_notebook_id_title_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX note_notebook_id_title_index ON note (notebook_id, title);
-
---
--- Name: password_reset_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX password_reset_id_index ON password_reset (id);
-
---
--- Name: password_reset_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX password_reset_email_address_index ON password_reset (email_address);
-
--- Name: download_access_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX download_access_id_index ON password_reset (id);
-
--- Name: download_access_transaction_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX download_access_transaction_id_index ON download_access (transaction_id);
-
---
--- Name: search_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
---
-
CREATE INDEX search_index ON note (search);
-
--- vim: ft=sql
diff --git a/model/test/Test_notebook.py b/model/test/Test_notebook.py
index c506605..3d7ee0b 100644
--- a/model/test/Test_notebook.py
+++ b/model/test/Test_notebook.py
@@ -12,11 +12,11 @@ class Test_notebook( object ):
self.trash_name = u"trash"
self.user_id = u"me"
self.delta = timedelta( seconds = 1 )
- self.read_write = True
+ self.read_write = Notebook.READ_WRITE
self.owner = False
self.rank = 17.5
- self.trash = Notebook.create( self.trash_id, self.trash_name, read_write = False, deleted = False, user_id = self.user_id )
+ self.trash = Notebook.create( self.trash_id, self.trash_name, read_write = Notebook.READ_ONLY, deleted = False, user_id = self.user_id )
self.notebook = Notebook.create( self.object_id, self.name, trash_id = self.trash.object_id, deleted = False, user_id = self.user_id, read_write = self.read_write, owner = self.owner, rank = self.rank )
self.note = Note.create( "19", u"title
blah" )
@@ -24,7 +24,6 @@ class Test_notebook( object ):
assert self.notebook.object_id == self.object_id
assert datetime.now( tz = utc ) - self.notebook.revision < self.delta
assert self.notebook.name == self.name
- assert self.notebook.read_write == True
assert self.notebook.trash_id == self.trash_id
assert self.notebook.deleted == False
assert self.notebook.user_id == self.user_id
@@ -35,14 +34,104 @@ class Test_notebook( object ):
assert self.trash.object_id == self.trash_id
assert datetime.now( tz = utc ) - self.trash.revision < self.delta
assert self.trash.name == self.trash_name
- assert self.trash.read_write == False
assert self.trash.trash_id == None
assert self.trash.deleted == False
assert self.trash.user_id == self.user_id
- assert self.trash.read_write == False
+ assert self.trash.read_write == Notebook.READ_ONLY
assert self.trash.owner == True
assert self.trash.rank == None
+ def test_create_read_write_true( self ):
+ notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = True, owner = self.owner, rank = self.rank )
+
+ assert notebook.object_id == self.object_id
+ assert datetime.now( tz = utc ) - notebook.revision < self.delta
+ assert notebook.name == self.name
+ assert notebook.trash_id == None
+ assert notebook.deleted == False
+ assert notebook.user_id == self.user_id
+ assert notebook.read_write == Notebook.READ_WRITE
+ assert notebook.owner == self.owner
+ assert notebook.rank == self.rank
+
+ def test_create_read_write_false( self ):
+ notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = False, owner = self.owner, rank = self.rank )
+
+ assert notebook.object_id == self.object_id
+ assert datetime.now( tz = utc ) - notebook.revision < self.delta
+ assert notebook.name == self.name
+ assert notebook.trash_id == None
+ assert notebook.deleted == False
+ assert notebook.user_id == self.user_id
+ assert notebook.read_write == Notebook.READ_ONLY
+ assert notebook.owner == self.owner
+ assert notebook.rank == self.rank
+
+ def test_create_read_write_none( self ):
+ notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = None, owner = self.owner, rank = self.rank )
+
+ assert notebook.object_id == self.object_id
+ assert datetime.now( tz = utc ) - notebook.revision < self.delta
+ assert notebook.name == self.name
+ assert notebook.trash_id == None
+ assert notebook.deleted == False
+ assert notebook.user_id == self.user_id
+ assert notebook.read_write == Notebook.READ_WRITE
+ assert notebook.owner == self.owner
+ assert notebook.rank == self.rank
+
+ def test_create_read_write_true_and_own_notes_only_true( self ):
+ notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = True, owner = self.owner, rank = self.rank, own_notes_only = True )
+
+ assert notebook.object_id == self.object_id
+ assert datetime.now( tz = utc ) - notebook.revision < self.delta
+ assert notebook.name == self.name
+ assert notebook.trash_id == None
+ assert notebook.deleted == False
+ assert notebook.user_id == self.user_id
+ assert notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES
+ assert notebook.owner == self.owner
+ assert notebook.rank == self.rank
+
+ def test_create_read_write_false_and_own_notes_only_true( self ):
+ notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = False, owner = self.owner, rank = self.rank, own_notes_only = True )
+
+ assert notebook.object_id == self.object_id
+ assert datetime.now( tz = utc ) - notebook.revision < self.delta
+ assert notebook.name == self.name
+ assert notebook.trash_id == None
+ assert notebook.deleted == False
+ assert notebook.user_id == self.user_id
+ assert notebook.read_write == Notebook.READ_ONLY
+ assert notebook.owner == self.owner
+ assert notebook.rank == self.rank
+
+ def test_create_read_write_false_and_own_notes_only_false( self ):
+ notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = False, owner = self.owner, rank = self.rank, own_notes_only = False )
+
+ assert notebook.object_id == self.object_id
+ assert datetime.now( tz = utc ) - notebook.revision < self.delta
+ assert notebook.name == self.name
+ assert notebook.trash_id == None
+ assert notebook.deleted == False
+ assert notebook.user_id == self.user_id
+ assert notebook.read_write == Notebook.READ_ONLY
+ assert notebook.owner == self.owner
+ assert notebook.rank == self.rank
+
+ def test_create_read_write_true_and_own_notes_only_false( self ):
+ notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = True, owner = self.owner, rank = self.rank, own_notes_only = False )
+
+ assert notebook.object_id == self.object_id
+ assert datetime.now( tz = utc ) - notebook.revision < self.delta
+ assert notebook.name == self.name
+ assert notebook.trash_id == None
+ assert notebook.deleted == False
+ assert notebook.user_id == self.user_id
+ assert notebook.read_write == Notebook.READ_WRITE
+ assert notebook.owner == self.owner
+ assert notebook.rank == self.rank
+
def test_set_name( self ):
new_name = u"my new notebook"
previous_revision = self.notebook.revision
@@ -52,10 +141,31 @@ class Test_notebook( object ):
assert self.notebook.revision > previous_revision
def test_set_read_write( self ):
+ original_revision = self.notebook.revision
+ self.notebook.read_write = Notebook.READ_WRITE_FOR_OWN_NOTES
+
+ assert self.notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES
+ assert self.notebook.revision == original_revision
+
+ def test_set_read_write_true( self ):
original_revision = self.notebook.revision
self.notebook.read_write = True
- assert self.notebook.read_write == True
+ assert self.notebook.read_write == Notebook.READ_WRITE
+ assert self.notebook.revision == original_revision
+
+ def test_set_read_write_false( self ):
+ original_revision = self.notebook.revision
+ self.notebook.read_write = False
+
+ assert self.notebook.read_write == Notebook.READ_ONLY
+ assert self.notebook.revision == original_revision
+
+ def test_set_read_write_none( self ):
+ original_revision = self.notebook.revision
+ self.notebook.read_write = None
+
+ assert self.notebook.read_write == Notebook.READ_WRITE
assert self.notebook.revision == original_revision
def test_set_deleted( self ):
@@ -84,7 +194,7 @@ class Test_notebook( object ):
assert d.get( "name" ) == self.name
assert d.get( "trash_id" ) == self.trash.object_id
- assert d.get( "read_write" ) == True
+ assert d.get( "read_write" ) == self.read_write
assert d.get( "deleted" ) == self.notebook.deleted
assert d.get( "user_id" ) == self.notebook.user_id
assert d.get( "object_id" ) == self.notebook.object_id
diff --git a/model/test/Test_tag.py b/model/test/Test_tag.py
new file mode 100644
index 0000000..a0aaab2
--- /dev/null
+++ b/model/test/Test_tag.py
@@ -0,0 +1,36 @@
+from pytz import utc
+from datetime import datetime, timedelta
+from model.Tag import Tag
+
+
+class Test_tag( object ):
+ def setUp( self ):
+ self.object_id = u"17"
+ self.notebook_id = u"19"
+ self.user_id = u"20"
+ self.name = u"mytag"
+ self.description = u"this is my tag"
+ self.value = u"a value"
+ self.delta = timedelta( seconds = 1 )
+
+ self.tag = Tag.create( self.object_id, self.notebook_id, self.user_id, self.name,
+ self.description, self.value )
+
+ def test_create( self ):
+ assert self.tag.object_id == self.object_id
+ assert self.tag.notebook_id == self.notebook_id
+ assert self.tag.user_id == self.user_id
+ assert self.tag.name == self.name
+ assert self.tag.description == self.description
+ assert self.tag.value == self.value
+
+ def test_to_dict( self ):
+ d = self.tag.to_dict()
+
+ assert d.get( "object_id" ) == self.object_id
+ assert datetime.now( tz = utc ) - d.get( "revision" ) < self.delta
+ assert d.get( "notebook_id" ) == self.notebook_id
+ assert d.get( "user_id" ) == self.user_id
+ assert d.get( "name" ) == self.name
+ assert d.get( "description" ) == self.description
+ assert d.get( "value" ) == self.value
diff --git a/setup.py b/setup.py
index 98f164a..ae20f1d 100644
--- a/setup.py
+++ b/setup.py
@@ -253,6 +253,7 @@ data_files = [
( "static/images/toolbar/small", files( "static/images/toolbar/small/*.*", excludes = [ "static/images/toolbar/small/*.xcf" ] ) ),
( "static/js", files( "static/js/*.*" ) ),
( "static/js", files( "static/js/*_LICENSE" ) ),
+ ( "model/delta", files( "model/delta/*.sqlite" ) ),
]
package_data = { ".": sum( [ pair[ 1 ] for pair in data_files ], [] ) }
diff --git a/static/html/support.html b/static/html/support.html
index b7c5564..ef69f34 100644
--- a/static/html/support.html
+++ b/static/html/support.html
@@ -20,14 +20,12 @@ The Luminotes user guide explains every feature of Luminotes in full detail,
and it even includes tips for organizing your wiki.
Previous revision from ' + short_revision + '
' + @@ -984,7 +1003,7 @@ Wiki.prototype.editor_mouse_hovered = function ( editor, target ) { } Wiki.prototype.key_pressed = function ( event ) { - if ( !this.notebook.read_write ) + if ( this.notebook.read_write == NOTEBOOK_READ_ONLY ) return; var code = event.key().code; @@ -1221,7 +1240,7 @@ Wiki.prototype.update_toolbar = function() { var link = null; // a read-only notebook doesn't have a visible toolbar - if ( !this.notebook.read_write ) + if ( this.notebook.read_write == NOTEBOOK_READ_ONLY ) return; if ( this.focused_editor ) { @@ -1332,7 +1351,7 @@ Wiki.prototype.hide_editor = function ( event, editor ) { this.display_empty_message(); } else { // before hiding an editor, save it - if ( this.notebook.read_write && editor.read_write ) { + if ( this.notebook.read_write != NOTEBOOK_READ_ONLY && editor.read_write ) { var self = this; this.save_editor( editor, false, function () { editor.shutdown(); @@ -1367,7 +1386,7 @@ Wiki.prototype.delete_editor = function ( event, editor ) { var self = this; this.save_editor( editor, false, function () { - if ( self.notebook.read_write && editor.read_write ) { + if ( self.notebook.read_write != NOTEBOOK_READ_ONLY && editor.read_write ) { self.invoker.invoke( "/notebooks/delete_note", "POST", { "notebook_id": self.notebook_id, "note_id": editor.id @@ -1414,7 +1433,7 @@ Wiki.prototype.undelete_editor_via_trash = function ( event, editor ) { if ( this.startup_notes[ editor.id ] ) delete this.startup_notes[ editor.id ]; - if ( this.notebook.read_write && editor.read_write ) { + if ( this.notebook.read_write != NOTEBOOK_READ_ONLY && editor.read_write ) { var self = this; this.invoker.invoke( "/notebooks/undelete_note", "POST", { "notebook_id": editor.deleted_from_id, @@ -1437,7 +1456,7 @@ Wiki.prototype.undelete_editor_via_trash = function ( event, editor ) { Wiki.prototype.undelete_editor_via_undo = function( event, editor, position_after ) { if ( editor ) { - if ( this.notebook.read_write && editor.read_write ) { + if ( this.notebook.read_write != NOTEBOOK_READ_ONLY && editor.read_write ) { var self = this; this.invoker.invoke( "/notebooks/undelete_note", "POST", { "notebook_id": this.notebook_id, @@ -1458,7 +1477,7 @@ Wiki.prototype.undelete_editor_via_undo = function( event, editor, position_afte } Wiki.prototype.undelete_editor_via_undelete = function( event, note_id, position_after ) { - if ( this.notebook.read_write ) { + if ( this.notebook.read_write != NOTEBOOK_READ_ONLY ) { var self = this; this.invoker.invoke( "/notebooks/undelete_note", "POST", { "notebook_id": this.notebook_id, @@ -1600,7 +1619,7 @@ Wiki.prototype.submit_form = function ( form ) { } } else if ( url == "/users/update_settings" ) { callback = function ( result ) { - self.email_address = result.email_address || ""; + self.user.email_address = result.email_address || ""; self.display_message( "Your account settings have been updated." ); } } else if ( url == "/users/signup_group_member" ) { @@ -1996,7 +2015,7 @@ Wiki.prototype.display_settings = function () { createDOM( "br", {} ), createDOM( "input", { "type": "text", "name": "email_address", "id": "email_address", "class": "text_field", - "size": "30", "maxlength": "60", "value": this.email_address || "" } + "size": "30", "maxlength": "60", "value": this.user.email_address || "" } ) ), createDOM( "p", {}, @@ -2510,7 +2529,7 @@ Wiki.prototype.delete_all_editors = function ( event ) { this.startup_notes = new Array(); - if ( this.notebook.read_write ) { + if ( this.notebook.read_write == NOTEBOOK_READ_WRITE ) { var self = this; this.invoker.invoke( "/notebooks/delete_all_notes", "POST", { "notebook_id": this.notebook_id diff --git a/tools/initdb.py b/tools/initdb.py index be8a717..3b32c95 100644 --- a/tools/initdb.py +++ b/tools/initdb.py @@ -50,7 +50,7 @@ class Initializer( object ): def create_main_notebook( self ): # create the main notebook main_notebook_id = self.database.next_id( Notebook ) - self.main_notebook = Notebook.create( main_notebook_id, u"Luminotes" ) + self.main_notebook = Notebook.create( main_notebook_id, u"Luminotes", rank = 0 ) self.database.save( self.main_notebook, commit = False ) # no need to create default notes for the desktop version diff --git a/tools/make_forum_thread.py b/tools/make_forum_thread.py new file mode 100755 index 0000000..5c8819e --- /dev/null +++ b/tools/make_forum_thread.py @@ -0,0 +1,113 @@ +#!/usr/bin/python2.4 + +import os +import os.path +import sys +import cherrypy +from datetime import datetime +from controller.Database import Database +from model.Notebook import Notebook +from model.Note import Note +from model.User import User +from model.Tag import Tag + + +class Thread_maker( object ): + """ + Create a thread for a new forum. + """ + def __init__( self, database, forum_name ): + self.database = database + self.forum_name = forum_name + + self.make_thread() + self.database.commit() + + def make_thread( self ): + title = u"Welcome to the Luminotes %s forum!" % self.forum_name + + # create a notebook thread to go in the forum + notebook_id = self.database.next_id( Notebook, commit = False ) + thread_notebook = Notebook.create( + notebook_id, + title, + ) + self.database.save( thread_notebook, commit = False ) + + anonymous = self.database.select_one( User, User.sql_load_by_username( u"anonymous" ) ) + + # add a single welcome note to the new thread + note_id = self.database.next_id( Note, commit = False ) + note = Note.create( + note_id, + u""" +