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:
parent
0c01d9143f
commit
4f3267b0f4
|
@ -672,9 +672,12 @@ class Notebooks( object ):
|
|||
contents = Valid_string( min = 1, max = 50000, escape_html = False ),
|
||||
startup = Valid_bool(),
|
||||
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 ),
|
||||
)
|
||||
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
|
||||
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
|
||||
@param previous_revision: previous known revision timestamp of the provided note, or None if
|
||||
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
|
||||
@param user_id: id of current logged-in user (if any), determined by @grab_user_id
|
||||
@rtype: json dict
|
||||
|
@ -699,7 +706,7 @@ class Notebooks( object ):
|
|||
'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
|
||||
'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 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:
|
||||
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
|
||||
def update_note( current_notebook, old_note, startup, user ):
|
||||
# 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
|
||||
# the note has changed, so update it
|
||||
else:
|
||||
note.contents = contents
|
||||
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 )
|
||||
|
||||
self.__files.purge_unused( note )
|
||||
|
@ -760,10 +780,10 @@ class Notebooks( object ):
|
|||
new_revision = update_note( notebook, old_note, startup, user )
|
||||
# otherwise, create a new note
|
||||
else:
|
||||
if startup:
|
||||
rank = self.__database.select_one( float, notebook.sql_highest_note_rank() ) + 1
|
||||
if position_after or position_before:
|
||||
note.rank = calculate_rank( position_after, position_before )
|
||||
else:
|
||||
rank = None
|
||||
rank = self.__database.select_one( float, notebook.sql_highest_note_rank() ) + 1
|
||||
|
||||
previous_revision = None
|
||||
note = Note.create( note_id, contents, notebook_id = notebook.object_id, startup = startup, rank = rank, user_id = user_id )
|
||||
|
|
|
@ -1105,6 +1105,7 @@ class Test_notebooks( Test_controller ):
|
|||
notebook_id = self.notebook.object_id,
|
||||
note_id = self.note.object_id,
|
||||
contents = new_note_contents,
|
||||
startup = False,
|
||||
), session_id = self.session_id )
|
||||
|
||||
# load the note by the old revision
|
||||
|
@ -1122,7 +1123,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert note.title == previous_title
|
||||
assert note.contents == previous_contents
|
||||
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 ):
|
||||
self.login()
|
||||
|
@ -1730,11 +1731,7 @@ class Test_notebooks( Test_controller ):
|
|||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes > 0
|
||||
assert result[ "storage_bytes" ] == user.storage_bytes
|
||||
|
||||
if startup:
|
||||
assert result[ "rank" ] == 0.0
|
||||
else:
|
||||
assert result[ "rank" ] is None
|
||||
assert result[ "rank" ] == 0.0
|
||||
|
||||
# make sure the old title can no longer be loaded
|
||||
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.startup == startup
|
||||
assert note.user_id == self.user.object_id
|
||||
|
||||
if startup:
|
||||
assert note.rank == 0.0
|
||||
else:
|
||||
assert note.rank is None
|
||||
assert note.rank == 0.0
|
||||
|
||||
# make sure that the correct revisions are returned and are in chronological order
|
||||
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" ].user_id == self.user.object_id
|
||||
assert result[ "previous_revision" ].username == self.username
|
||||
assert result[ "rank" ] is None
|
||||
assert result[ "rank" ] == 0.0
|
||||
|
||||
self.login()
|
||||
|
||||
|
@ -1898,11 +1891,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert note.contents == new_note_contents
|
||||
assert note.startup == startup
|
||||
assert note.user_id == self.user2.object_id
|
||||
|
||||
if startup:
|
||||
assert note.rank == 0.0
|
||||
else:
|
||||
assert note.rank is None
|
||||
assert note.rank == 0.0
|
||||
|
||||
# make sure that the correct revisions are returned and are in chronological order
|
||||
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 )
|
||||
assert user.storage_bytes > 0
|
||||
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
|
||||
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.startup == startup
|
||||
assert note.user_id == self.user.object_id
|
||||
|
||||
if startup:
|
||||
assert note.rank == 0.0
|
||||
else:
|
||||
assert note.rank is None
|
||||
assert note.rank == 0.0
|
||||
|
||||
# make sure that the correct revisions are returned and are in chronological order
|
||||
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 )
|
||||
assert user.storage_bytes > 0
|
||||
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
|
||||
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 )
|
||||
assert user.storage_bytes == previous_storage_bytes
|
||||
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(
|
||||
notebook_id = self.notebook.object_id,
|
||||
|
@ -2209,7 +2194,7 @@ class Test_notebooks( Test_controller ):
|
|||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == previous_storage_bytes
|
||||
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(
|
||||
notebook_id = self.notebook.object_id,
|
||||
|
@ -2325,7 +2310,7 @@ class Test_notebooks( Test_controller ):
|
|||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == previous_storage_bytes
|
||||
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(
|
||||
notebook_id = self.notebook.object_id,
|
||||
|
@ -2377,7 +2362,7 @@ class Test_notebooks( Test_controller ):
|
|||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes > 0
|
||||
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
|
||||
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 )
|
||||
assert user.storage_bytes > 0
|
||||
assert result[ "storage_bytes" ] == user.storage_bytes
|
||||
|
||||
if startup:
|
||||
assert result[ "rank" ] == 0.0
|
||||
else:
|
||||
assert result[ "rank" ] is None
|
||||
assert result[ "rank" ] == 0.0
|
||||
|
||||
# make sure the new title is now loadable
|
||||
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.startup == startup
|
||||
assert note.user_id == self.user.object_id
|
||||
|
||||
if startup:
|
||||
assert note.rank == 0.0
|
||||
else:
|
||||
assert note.rank is None
|
||||
assert note.rank == 0.0
|
||||
|
||||
def test_save_new_startup_note( self ):
|
||||
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 )
|
||||
assert user.storage_bytes > 0
|
||||
assert result[ "storage_bytes" ] == user.storage_bytes
|
||||
assert result[ "rank" ] == None
|
||||
assert result[ "rank" ] == 0.0
|
||||
|
||||
# make sure the new title is now loadable
|
||||
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 )
|
||||
assert user.storage_bytes > 0
|
||||
assert result[ "storage_bytes" ] == user.storage_bytes
|
||||
assert result[ "rank" ] == None
|
||||
assert result[ "rank" ] == 0.0
|
||||
|
||||
# make sure the new title is now loadable
|
||||
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 )
|
||||
assert user.storage_bytes > 0
|
||||
assert result[ "storage_bytes" ] == user.storage_bytes
|
||||
|
||||
if startup:
|
||||
assert result[ "rank" ] == 1.0
|
||||
else:
|
||||
assert result[ "rank" ] is None
|
||||
assert result[ "rank" ] == 1.0
|
||||
|
||||
# make sure the new title is now loadable
|
||||
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.startup == startup
|
||||
assert note.user_id == self.user.object_id
|
||||
|
||||
if startup:
|
||||
assert note.rank == 1 # one greater than the previous new note's rank
|
||||
else:
|
||||
assert note.rank is None
|
||||
assert note.rank == 1.0 # one greater than the previous new note's rank
|
||||
|
||||
def test_save_two_new_startup_notes( self ):
|
||||
self.test_save_two_new_notes( startup = True )
|
||||
|
|
|
@ -848,6 +848,7 @@ Editor.prototype.drop = function( event ) {
|
|||
swapDOM( hover_drop_target, this.holder );
|
||||
removeElement( "note_drag_source_area" );
|
||||
this.highlight();
|
||||
signal( this, "moved", this, this.previous_editor(), this.next_editor() );
|
||||
}
|
||||
|
||||
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 ) {
|
||||
signal( this, "key_pressed", this, event );
|
||||
|
||||
|
|
|
@ -849,6 +849,9 @@ Wiki.prototype.create_editor = function ( id, note_text, deleted_from_id, revisi
|
|||
connect( editor, "focused", this, "editor_focused" );
|
||||
connect( editor, "mouse_hovered", function ( target ) { self.editor_mouse_hovered( editor, target ) } );
|
||||
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" );
|
||||
|
@ -1038,6 +1041,10 @@ Wiki.prototype.editor_focused = function ( editor, synchronous ) {
|
|||
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 ) {
|
||||
if ( username == "anonymous" )
|
||||
username = "admin";
|
||||
|
@ -1205,15 +1212,9 @@ Wiki.prototype.focus_previous_editor = function () {
|
|||
return;
|
||||
}
|
||||
|
||||
var previous_holder = this.focused_editor.holder.previousSibling;
|
||||
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;
|
||||
|
||||
var previous_editor = this.focused_editor.previous_editor();
|
||||
this.editor_focused( null );
|
||||
div.editor.highlight();
|
||||
previous_editor.highlight();
|
||||
}
|
||||
|
||||
Wiki.prototype.focus_next_editor = function () {
|
||||
|
@ -1224,15 +1225,9 @@ Wiki.prototype.focus_next_editor = function () {
|
|||
return;
|
||||
}
|
||||
|
||||
var next_holder = this.focused_editor.holder.nextSibling;
|
||||
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;
|
||||
|
||||
var next_editor = this.focused_editor.next_editor();
|
||||
this.editor_focused( null );
|
||||
div.editor.highlight();
|
||||
next_editor.highlight();
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
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 )
|
||||
editor = this.focused_editor;
|
||||
|
||||
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();
|
||||
|
||||
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,
|
||||
"contents": editor.contents(),
|
||||
"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 ) {
|
||||
self.update_editor_revisions( result, editor );
|
||||
self.display_storage_usage( result.storage_bytes );
|
||||
|
|
Reference in New Issue