File renaming works. Unit tests still pending.
File deleting implemented. Testing and unit tests still pending. Now releasing session lock at top of download() to prevent session deadlocks.
This commit is contained in:
parent
1f223089df
commit
eb18b6020d
|
@ -232,6 +232,10 @@ class Files( object ):
|
|||
@return: file data
|
||||
@raise Access_error: the current user doesn't have access to the notebook that the file is in
|
||||
"""
|
||||
# release the session lock before beginning to stream the download. otherwise, if the
|
||||
# upload is cancelled before it's done, the lock won't be released
|
||||
cherrypy.session.release_lock()
|
||||
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id ):
|
||||
|
@ -431,8 +435,8 @@ class Files( object ):
|
|||
)
|
||||
def stats( self, file_id, user_id = None ):
|
||||
"""
|
||||
Return information on a file that has been completely uploaded and is stored in the database.
|
||||
Also return the user's current storage utilization in bytes.
|
||||
Return information on a file that has been completely uploaded with its metadata stored in the
|
||||
database. Also return the user's current storage utilization in bytes.
|
||||
|
||||
@type file_id: unicode
|
||||
@param file_id: id of the file to report on
|
||||
|
@ -442,7 +446,7 @@ class Files( object ):
|
|||
@return: {
|
||||
'filename': filename,
|
||||
'size_bytes': filesize,
|
||||
'storage_bytes': current sturage usage by user
|
||||
'storage_bytes': current storage usage by user
|
||||
}
|
||||
@raise Access_error: the current user doesn't have access to the notebook that the file is in
|
||||
"""
|
||||
|
@ -461,8 +465,70 @@ class Files( object ):
|
|||
storage_bytes = user.storage_bytes,
|
||||
)
|
||||
|
||||
def delete( self, file_id ):
|
||||
pass # TODO
|
||||
@expose( view = Json )
|
||||
@grab_user_id
|
||||
@validate(
|
||||
file_id = Valid_id(),
|
||||
user_id = Valid_id( none_okay = True ),
|
||||
)
|
||||
def delete( self, file_id, user_id = None ):
|
||||
"""
|
||||
Delete a file that has been completely uploaded, removing both its metadata from the database
|
||||
and its data from the filesystem. Return the user's current storage utilization in bytes.
|
||||
|
||||
def rename( self, file_id, filename ):
|
||||
pass # TODO
|
||||
@type file_id: unicode
|
||||
@param file_id: id of the file to delete
|
||||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of current logged-in user (if any)
|
||||
@rtype: dict
|
||||
@return: {
|
||||
'storage_bytes': current storage usage by user
|
||||
}
|
||||
@raise Access_error: the current user doesn't have access to the notebook that the file is in
|
||||
"""
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
|
||||
user = self.__database.load( User, user_id )
|
||||
if not user:
|
||||
raise Access_error()
|
||||
|
||||
self.__database.execute( db_file.sql_delete() )
|
||||
os.remove( Upload_file.make_server_filename( file_id ) )
|
||||
|
||||
return dict(
|
||||
storage_bytes = user.storage_bytes,
|
||||
)
|
||||
|
||||
@expose( view = Json )
|
||||
@grab_user_id
|
||||
@validate(
|
||||
file_id = Valid_id(),
|
||||
filename = unicode,
|
||||
user_id = Valid_id( none_okay = True ),
|
||||
)
|
||||
def rename( self, file_id, filename, user_id = None ):
|
||||
"""
|
||||
Rename a file that has been completely uploaded.
|
||||
|
||||
@type file_id: unicode
|
||||
@param file_id: id of the file to delete
|
||||
@type filename: unicode
|
||||
@param filename: new name for the file
|
||||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of current logged-in user (if any)
|
||||
@rtype: dict
|
||||
@return: {}
|
||||
@raise Access_error: the current user doesn't have access to the notebook that the file is in
|
||||
"""
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
|
||||
db_file.filename = filename
|
||||
self.__database.save( db_file )
|
||||
|
||||
return dict()
|
||||
|
|
|
@ -95,6 +95,9 @@ class File( Persistent ):
|
|||
( quote( self.revision ), quote( self.__notebook_id ), quote( self.__note_id ), quote( self.__filename ),
|
||||
self.__size_bytes or 'null', quote( self.__content_type ), quote( self.object_id ) )
|
||||
|
||||
def sql_delete( self ):
|
||||
return "delete from file where file_id = %s;" % quote( self.object_id )
|
||||
|
||||
def to_dict( self ):
|
||||
d = Persistent.to_dict( self )
|
||||
d.update( dict(
|
||||
|
@ -107,8 +110,11 @@ class File( Persistent ):
|
|||
|
||||
return d
|
||||
|
||||
def __set_filename( self, filename ):
|
||||
self.__filename = filename
|
||||
|
||||
notebook_id = property( lambda self: self.__notebook_id )
|
||||
note_id = property( lambda self: self.__note_id )
|
||||
filename = property( lambda self: self.__filename )
|
||||
filename = property( lambda self: self.__filename, __set_filename )
|
||||
size_bytes = property( lambda self: self.__size_bytes )
|
||||
content_type = property( lambda self: self.__content_type )
|
||||
|
|
|
@ -2395,27 +2395,40 @@ function File_link_pulldown( wiki, notebook_id, invoker, editor, link ) {
|
|||
connect( this.filename_field, "onblur", function ( event ) { self.filename_field_changed( event ); } );
|
||||
connect( this.filename_field, "onkeydown", function ( event ) { self.filename_field_key_pressed( event ); } );
|
||||
|
||||
var delete_button = createDOM( "input", {
|
||||
"type": "button",
|
||||
"class": "button",
|
||||
"value": "delete",
|
||||
"title": "delete file"
|
||||
} );
|
||||
|
||||
appendChildNodes( this.div, createDOM( "span", { "class": "field_label" }, "filename: " ) );
|
||||
appendChildNodes( this.div, this.filename_field );
|
||||
appendChildNodes( this.div, this.file_size );
|
||||
appendChildNodes( this.div, " " );
|
||||
appendChildNodes( this.div, delete_button );
|
||||
|
||||
var query = parse_query( link );
|
||||
var file_id = query.file_id;
|
||||
this.file_id = query.file_id;
|
||||
|
||||
// get the file's name and size from the server
|
||||
this.invoker.invoke(
|
||||
"/files/stats", "GET", {
|
||||
"file_id": file_id
|
||||
"file_id": this.file_id
|
||||
},
|
||||
function ( result ) {
|
||||
// if the user has already started typing something, don't overwrite it
|
||||
if ( self.filename_field.value.length == 0 )
|
||||
if ( self.filename_field.value.length == 0 ) {
|
||||
self.filename_field.value = result.filename;
|
||||
self.previous_filename = result.filename;
|
||||
}
|
||||
replaceChildNodes( self.file_size, bytes_to_megabytes( result.size_bytes, true ) );
|
||||
self.wiki.display_storage_usage( result.storage_bytes );
|
||||
}
|
||||
);
|
||||
|
||||
connect( delete_button, "onclick", function ( event ) { self.delete_button_clicked( event ); } );
|
||||
|
||||
// FIXME: when this is called, the text cursor moves to an unexpected location
|
||||
editor.focus();
|
||||
}
|
||||
|
@ -2437,11 +2450,15 @@ File_link_pulldown.prototype.filename_field_changed = function ( event ) {
|
|||
if ( filename == this.previous_filename )
|
||||
return;
|
||||
|
||||
var title = link_title( this.link );
|
||||
if ( title == this.previous_filename )
|
||||
replaceChildNodes( this.link, this.editor.document.createTextNode( filename ) );
|
||||
|
||||
this.previous_filename = filename;
|
||||
|
||||
this.invoker.invoke(
|
||||
"/files/rename", "GET", {
|
||||
"file_id": file_id,
|
||||
"/files/rename", "POST", {
|
||||
"file_id": this.file_id,
|
||||
"filename": filename
|
||||
}
|
||||
);
|
||||
|
@ -2456,6 +2473,17 @@ File_link_pulldown.prototype.filename_field_key_pressed = function ( event ) {
|
|||
}
|
||||
}
|
||||
|
||||
File_link_pulldown.prototype.delete_button_clicked = function ( event ) {
|
||||
var self = this;
|
||||
|
||||
this.invoker.invoke(
|
||||
"/files/delete", "POST", {
|
||||
"file_id": this.file_id
|
||||
},
|
||||
function ( result ) { self.wiki.display_storage_usage( result.storage_bytes ); }
|
||||
);
|
||||
}
|
||||
|
||||
File_link_pulldown.prototype.update_position = function ( anchor, relative_to ) {
|
||||
Pulldown.prototype.update_position.call( this, anchor, relative_to );
|
||||
}
|
||||
|
|
Reference in New Issue