witten
/
luminotes
Archived
1
0
Fork 0

Now note reordering is saved to the server. Still to do: New unit tests for reordering. Re-rank all notes in notebook when necessary.

This commit is contained in:
Dan Helfman 2009-02-10 01:32:08 -08:00
parent 0c01d9143f
commit 4f3267b0f4
4 changed files with 91 additions and 81 deletions

View File

@ -672,9 +672,12 @@ class Notebooks( object ):
contents = Valid_string( min = 1, max = 50000, escape_html = False ), contents = Valid_string( min = 1, max = 50000, escape_html = False ),
startup = Valid_bool(), startup = Valid_bool(),
previous_revision = Valid_revision( none_okay = True ), previous_revision = Valid_revision( none_okay = True ),
position_after = Valid_id( none_okay = True ),
position_before = Valid_id( none_okay = True ),
user_id = Valid_id( none_okay = True ), user_id = Valid_id( none_okay = True ),
) )
def save_note( self, notebook_id, note_id, contents, startup, previous_revision, user_id ): def save_note( self, notebook_id, note_id, contents, startup, previous_revision = None,
position_after = None, position_before = None, user_id = None ):
""" """
Save a new revision of the given note. This function will work both for creating a new note and Save a new revision of the given note. This function will work both for creating a new note and
for updating an existing note. If the note exists and the given contents are identical to the for updating an existing note. If the note exists and the given contents are identical to the
@ -692,6 +695,10 @@ class Notebooks( object ):
@type previous_revision: unicode or NoneType @type previous_revision: unicode or NoneType
@param previous_revision: previous known revision timestamp of the provided note, or None if @param previous_revision: previous known revision timestamp of the provided note, or None if
the note is new the note is new
@type position_after: unicode or NoneType
@param position_after: id of note to position the saved note after (optional)
@type position_before: unicode or NoneType
@param position_before: id of note to position the saved note before (optional)
@type user_id: unicode or NoneType @type user_id: unicode or NoneType
@param user_id: id of current logged-in user (if any), determined by @grab_user_id @param user_id: id of current logged-in user (if any), determined by @grab_user_id
@rtype: json dict @rtype: json dict
@ -699,7 +706,7 @@ class Notebooks( object ):
'new_revision': User_revision of saved note, or None if nothing was saved 'new_revision': User_revision of saved note, or None if nothing was saved
'previous_revision': User_revision immediately before new_revision, or None if the note is new 'previous_revision': User_revision immediately before new_revision, or None if the note is new
'storage_bytes': current storage usage by user 'storage_bytes': current storage usage by user
'rank': integer rank of the saved note, or None 'rank': float rank of the saved note, or None
} }
@raise Access_error: the current user doesn't have access to the given notebook @raise Access_error: the current user doesn't have access to the given notebook
@raise Validation_error: one of the arguments is invalid @raise Validation_error: one of the arguments is invalid
@ -717,22 +724,35 @@ class Notebooks( object ):
if notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES: if notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES:
startup = True startup = True
def calculate_rank( position_after, position_before ):
after_note = position_after and self.__database.load( Note, position_after ) or None
before_note = position_before and self.__database.load( Note, position_before ) or None
if after_note and before_note:
return ( float( after_note.rank ) + float( before_note.rank ) ) / 2.0
elif after_note:
return float( after_note.rank ) + 1.0
elif before_note:
return max( float( before_note.rank ) - 1.0, 0.0 )
return 0.0
# check whether the provided note contents have been changed since the previous revision # check whether the provided note contents have been changed since the previous revision
def update_note( current_notebook, old_note, startup, user ): def update_note( current_notebook, old_note, startup, user ):
# the note hasn't been changed, so bail without updating it # the note hasn't been changed, so bail without updating it
if contents.replace( u"\n", u"" ) == old_note.contents.replace( u"\n", "" ) and startup == old_note.startup: if not position_after and not position_before and startup == old_note.startup and \
contents.replace( u"\n", u"" ) == old_note.contents.replace( u"\n", "" ):
new_revision = None new_revision = None
# the note has changed, so update it # the note has changed, so update it
else: else:
note.contents = contents note.contents = contents
note.startup = startup note.startup = startup
if startup:
if note.rank is None:
note.rank = self.__database.select_one( float, notebook.sql_highest_note_rank() ) + 1
else:
note.rank = None
note.user_id = user.object_id
if position_after or position_before:
note.rank = calculate_rank( position_after, position_before )
elif note.rank is None:
note.rank = self.__database.select_one( float, notebook.sql_highest_note_rank() ) + 1
note.user_id = user.object_id
new_revision = User_revision( note.revision, note.user_id, user.username ) new_revision = User_revision( note.revision, note.user_id, user.username )
self.__files.purge_unused( note ) self.__files.purge_unused( note )
@ -760,10 +780,10 @@ class Notebooks( object ):
new_revision = update_note( notebook, old_note, startup, user ) new_revision = update_note( notebook, old_note, startup, user )
# otherwise, create a new note # otherwise, create a new note
else: else:
if startup: if position_after or position_before:
rank = self.__database.select_one( float, notebook.sql_highest_note_rank() ) + 1 note.rank = calculate_rank( position_after, position_before )
else: else:
rank = None rank = self.__database.select_one( float, notebook.sql_highest_note_rank() ) + 1
previous_revision = None previous_revision = None
note = Note.create( note_id, contents, notebook_id = notebook.object_id, startup = startup, rank = rank, user_id = user_id ) note = Note.create( note_id, contents, notebook_id = notebook.object_id, startup = startup, rank = rank, user_id = user_id )

View File

@ -1105,6 +1105,7 @@ class Test_notebooks( Test_controller ):
notebook_id = self.notebook.object_id, notebook_id = self.notebook.object_id,
note_id = self.note.object_id, note_id = self.note.object_id,
contents = new_note_contents, contents = new_note_contents,
startup = False,
), session_id = self.session_id ) ), session_id = self.session_id )
# load the note by the old revision # load the note by the old revision
@ -1122,7 +1123,7 @@ class Test_notebooks( Test_controller ):
assert note.title == previous_title assert note.title == previous_title
assert note.contents == previous_contents assert note.contents == previous_contents
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0 assert user.storage_bytes > 0
def test_load_note_with_previous_revision( self ): def test_load_note_with_previous_revision( self ):
self.login() self.login()
@ -1730,11 +1731,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0 assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] == 0.0
if startup:
assert result[ "rank" ] == 0.0
else:
assert result[ "rank" ] is None
# make sure the old title can no longer be loaded # make sure the old title can no longer be loaded
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -1758,11 +1755,7 @@ class Test_notebooks( Test_controller ):
assert note.contents == new_note_contents assert note.contents == new_note_contents
assert note.startup == startup assert note.startup == startup
assert note.user_id == self.user.object_id assert note.user_id == self.user.object_id
assert note.rank == 0.0
if startup:
assert note.rank == 0.0
else:
assert note.rank is None
# make sure that the correct revisions are returned and are in chronological order # make sure that the correct revisions are returned and are in chronological order
result = self.http_post( "/notebooks/load_note_revisions/", dict( result = self.http_post( "/notebooks/load_note_revisions/", dict(
@ -1872,7 +1865,7 @@ class Test_notebooks( Test_controller ):
assert result[ "previous_revision" ].revision == previous_revision assert result[ "previous_revision" ].revision == previous_revision
assert result[ "previous_revision" ].user_id == self.user.object_id assert result[ "previous_revision" ].user_id == self.user.object_id
assert result[ "previous_revision" ].username == self.username assert result[ "previous_revision" ].username == self.username
assert result[ "rank" ] is None assert result[ "rank" ] == 0.0
self.login() self.login()
@ -1898,11 +1891,7 @@ class Test_notebooks( Test_controller ):
assert note.contents == new_note_contents assert note.contents == new_note_contents
assert note.startup == startup assert note.startup == startup
assert note.user_id == self.user2.object_id assert note.user_id == self.user2.object_id
assert note.rank == 0.0
if startup:
assert note.rank == 0.0
else:
assert note.rank is None
# make sure that the correct revisions are returned and are in chronological order # make sure that the correct revisions are returned and are in chronological order
result = self.http_post( "/notebooks/load_note_revisions/", dict( result = self.http_post( "/notebooks/load_note_revisions/", dict(
@ -2004,7 +1993,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0 assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] is None assert result[ "rank" ] == 0.0
# make sure the old title can no longer be loaded # make sure the old title can no longer be loaded
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -2028,11 +2017,7 @@ class Test_notebooks( Test_controller ):
assert note.contents == new_note_contents.replace( u"<span>", "" ) assert note.contents == new_note_contents.replace( u"<span>", "" )
assert note.startup == startup assert note.startup == startup
assert note.user_id == self.user.object_id assert note.user_id == self.user.object_id
assert note.rank == 0.0
if startup:
assert note.rank == 0.0
else:
assert note.rank is None
# make sure that the correct revisions are returned and are in chronological order # make sure that the correct revisions are returned and are in chronological order
result = self.http_post( "/notebooks/load_note_revisions/", dict( result = self.http_post( "/notebooks/load_note_revisions/", dict(
@ -2081,7 +2066,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0 assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] is None assert result[ "rank" ] == 0.0
# make sure the old title can no longer be loaded # make sure the old title can no longer be loaded
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -2155,7 +2140,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == previous_storage_bytes assert user.storage_bytes == previous_storage_bytes
assert result[ "storage_bytes" ] == 0 assert result[ "storage_bytes" ] == 0
assert result[ "rank" ] is None assert result[ "rank" ] == 0.0
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
notebook_id = self.notebook.object_id, notebook_id = self.notebook.object_id,
@ -2209,7 +2194,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == previous_storage_bytes assert user.storage_bytes == previous_storage_bytes
assert result[ "storage_bytes" ] == 0 assert result[ "storage_bytes" ] == 0
assert result[ "rank" ] is None assert result[ "rank" ] == 0.0
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
notebook_id = self.notebook.object_id, notebook_id = self.notebook.object_id,
@ -2325,7 +2310,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == previous_storage_bytes assert user.storage_bytes == previous_storage_bytes
assert result[ "storage_bytes" ] == 0 assert result[ "storage_bytes" ] == 0
assert result[ "rank" ] is None assert result[ "rank" ] == 0.0
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
notebook_id = self.notebook.object_id, notebook_id = self.notebook.object_id,
@ -2377,7 +2362,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0 assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] is None assert result[ "rank" ] == 0.0
# make sure the first title can no longer be loaded # make sure the first title can no longer be loaded
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -2450,11 +2435,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0 assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] == 0.0
if startup:
assert result[ "rank" ] == 0.0
else:
assert result[ "rank" ] is None
# make sure the new title is now loadable # make sure the new title is now loadable
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -2469,11 +2450,7 @@ class Test_notebooks( Test_controller ):
assert note.contents == new_note.contents assert note.contents == new_note.contents
assert note.startup == startup assert note.startup == startup
assert note.user_id == self.user.object_id assert note.user_id == self.user.object_id
assert note.rank == 0.0
if startup:
assert note.rank == 0.0
else:
assert note.rank is None
def test_save_new_startup_note( self ): def test_save_new_startup_note( self ):
self.test_save_new_note( startup = True ) self.test_save_new_note( startup = True )
@ -2547,7 +2524,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0 assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] == None assert result[ "rank" ] == 0.0
# make sure the new title is now loadable # make sure the new title is now loadable
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -2588,7 +2565,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0 assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] == None assert result[ "rank" ] == 0.0
# make sure the new title is now loadable # make sure the new title is now loadable
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -2639,11 +2616,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id ) user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0 assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] == 1.0
if startup:
assert result[ "rank" ] == 1.0
else:
assert result[ "rank" ] is None
# make sure the new title is now loadable # make sure the new title is now loadable
result = self.http_post( "/notebooks/load_note_by_title/", dict( result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -2658,11 +2631,7 @@ class Test_notebooks( Test_controller ):
assert note.contents == new_note.contents assert note.contents == new_note.contents
assert note.startup == startup assert note.startup == startup
assert note.user_id == self.user.object_id assert note.user_id == self.user.object_id
assert note.rank == 1.0 # one greater than the previous new note's rank
if startup:
assert note.rank == 1 # one greater than the previous new note's rank
else:
assert note.rank is None
def test_save_two_new_startup_notes( self ): def test_save_two_new_startup_notes( self ):
self.test_save_two_new_notes( startup = True ) self.test_save_two_new_notes( startup = True )

View File

@ -848,6 +848,7 @@ Editor.prototype.drop = function( event ) {
swapDOM( hover_drop_target, this.holder ); swapDOM( hover_drop_target, this.holder );
removeElement( "note_drag_source_area" ); removeElement( "note_drag_source_area" );
this.highlight(); this.highlight();
signal( this, "moved", this, this.previous_editor(), this.next_editor() );
} }
var drop_targets = getElementsByTagAndClassName( "div", "note_drop_target" ); var drop_targets = getElementsByTagAndClassName( "div", "note_drop_target" );
@ -863,6 +864,28 @@ Editor.prototype.drop = function( event ) {
} }
} }
Editor.prototype.previous_editor = function () {
var previous_holder = this.holder.previousSibling;
while ( previous_holder && previous_holder.nodeValue == "\n" )
previous_holder = previous_holder.previousSibling;
if ( !previous_holder || !hasElementClass( previous_holder, "note_holder" ) ) return null;
var div = getFirstElementByTagAndClassName( "div", "static_note_div", previous_holder );
if ( !div || !div.editor ) return null;
return div.editor;
}
Editor.prototype.next_editor = function () {
var next_holder = this.holder.nextSibling;
while ( next_holder && next_holder.nodeValue == "\n" )
next_holder = next_holder.nextSibling;
if ( !next_holder || !hasElementClass( next_holder, "note_holder" ) ) return null;
var div = getFirstElementByTagAndClassName( "div", "static_note_div", next_holder );
if ( !div || !div.editor ) return null;
return div.editor;
}
Editor.prototype.key_pressed = function ( event ) { Editor.prototype.key_pressed = function ( event ) {
signal( this, "key_pressed", this, event ); signal( this, "key_pressed", this, event );

View File

@ -849,6 +849,9 @@ Wiki.prototype.create_editor = function ( id, note_text, deleted_from_id, revisi
connect( editor, "focused", this, "editor_focused" ); connect( editor, "focused", this, "editor_focused" );
connect( editor, "mouse_hovered", function ( target ) { self.editor_mouse_hovered( editor, target ) } ); connect( editor, "mouse_hovered", function ( target ) { self.editor_mouse_hovered( editor, target ) } );
connect( editor, "grabber_pressed", function ( event ) { self.editor_focused( null ); } ); connect( editor, "grabber_pressed", function ( event ) { self.editor_focused( null ); } );
connect( editor, "moved", function ( editor, position_after, position_before ) {
self.editor_moved( editor, position_after, position_before );
} );
} }
connect( editor, "load_editor", this, "load_editor" ); connect( editor, "load_editor", this, "load_editor" );
@ -1038,6 +1041,10 @@ Wiki.prototype.editor_focused = function ( editor, synchronous ) {
this.update_toolbar(); this.update_toolbar();
} }
Wiki.prototype.editor_moved = function ( editor, position_after, position_before ) {
this.save_editor( editor, false, null, null, null, position_after, position_before );
}
Wiki.prototype.make_byline = function ( username, creation, note_id ) { Wiki.prototype.make_byline = function ( username, creation, note_id ) {
if ( username == "anonymous" ) if ( username == "anonymous" )
username = "admin"; username = "admin";
@ -1205,15 +1212,9 @@ Wiki.prototype.focus_previous_editor = function () {
return; return;
} }
var previous_holder = this.focused_editor.holder.previousSibling; var previous_editor = this.focused_editor.previous_editor();
while ( previous_holder && previous_holder.nodeValue == "\n" )
previous_holder = previous_holder.previousSibling;
if ( !previous_holder || !hasElementClass( previous_holder, "note_holder" ) ) return;
var div = getFirstElementByTagAndClassName( "div", "static_note_div", previous_holder );
if ( !div || !div.editor ) return;
this.editor_focused( null ); this.editor_focused( null );
div.editor.highlight(); previous_editor.highlight();
} }
Wiki.prototype.focus_next_editor = function () { Wiki.prototype.focus_next_editor = function () {
@ -1224,15 +1225,9 @@ Wiki.prototype.focus_next_editor = function () {
return; return;
} }
var next_holder = this.focused_editor.holder.nextSibling; var next_editor = this.focused_editor.next_editor();
while ( next_holder && next_holder.nodeValue == "\n" )
next_holder = next_holder.nextSibling;
if ( !next_holder || !hasElementClass( next_holder, "note_holder" ) ) return;
var div = getFirstElementByTagAndClassName( "div", "static_note_div", next_holder );
if ( !div || !div.editor ) return;
this.editor_focused( null ); this.editor_focused( null );
div.editor.highlight(); next_editor.highlight();
} }
Wiki.prototype.get_toolbar_image_dir = function ( always_small ) { Wiki.prototype.get_toolbar_image_dir = function ( always_small ) {
@ -1694,12 +1689,13 @@ Wiki.prototype.compare_versions = function( event, editor, previous_revision ) {
this.load_editor( editor.title, editor.id, editor.revision, previous_revision, editor.closed ? null : editor.holder ); this.load_editor( editor.title, editor.id, editor.revision, previous_revision, editor.closed ? null : editor.holder );
} }
Wiki.prototype.save_editor = function ( editor, fire_and_forget, callback, synchronous, suppress_save_signal ) { Wiki.prototype.save_editor = function ( editor, fire_and_forget, callback, synchronous, suppress_save_signal, position_after, position_before ) {
if ( !editor ) if ( !editor )
editor = this.focused_editor; editor = this.focused_editor;
var self = this; var self = this;
if ( editor && editor.read_write && !editor.empty() && !editor.closed && editor.dirty() ) { if ( editor && editor.read_write && !editor.empty() && !editor.closed &&
( editor.dirty() || position_after || position_before ) ) {
editor.scrape_title(); editor.scrape_title();
this.invoker.invoke( "/notebooks/save_note", "POST", { this.invoker.invoke( "/notebooks/save_note", "POST", {
@ -1707,7 +1703,9 @@ Wiki.prototype.save_editor = function ( editor, fire_and_forget, callback, synch
"note_id": editor.id, "note_id": editor.id,
"contents": editor.contents(), "contents": editor.contents(),
"startup": editor.startup, "startup": editor.startup,
"previous_revision": editor.revision ? editor.revision : "None" "previous_revision": editor.revision ? editor.revision : "None",
"position_after": position_after ? position_after.id : "None",
"position_before": position_before ? position_before.id : "None"
}, function ( result ) { }, function ( result ) {
self.update_editor_revisions( result, editor ); self.update_editor_revisions( result, editor );
self.display_storage_usage( result.storage_bytes ); self.display_storage_usage( result.storage_bytes );