witten
/
luminotes
Archived
1
0
Fork 0

When saving a note, auto-delete any files that used to be linked from it but no longer are. Still need unit tests.

This commit is contained in:
Dan Helfman 2008-02-22 19:47:28 +00:00
parent 7b8f6bd6e5
commit 65ce915755
7 changed files with 57 additions and 19 deletions

View File

@ -1,16 +0,0 @@
import cherrypy
def async( method ):
"""
A decorator for a generator method that causes it to be invoked asynchronously. In other words,
whenever a generator method decorated by this decorator is called, its generator is added to
the scheduler for later execution.
This decorator expects a self.scheduler member containing the scheduler to use.
"""
def schedule( self, *args, **kwargs ):
thread = method( self, *args, **kwargs )
self.scheduler.add( thread )
return schedule

View File

@ -1,4 +1,5 @@
import os
import re
import cgi
import time
import tempfile
@ -197,6 +198,8 @@ cherrypy._cpcgifs.FieldStorage = FieldStorage
class Files( object ):
FILE_LINK_PATTERN = re.compile( u'<a\s+href="[^"]*/files/download\?file_id=([^"]+)">', re.IGNORECASE )
"""
Controller for dealing with uploaded files, corresponding to the "/files" URL.
"""
@ -527,3 +530,29 @@ class Files( object ):
self.__database.save( db_file )
return dict()
def purge_unused( self, note ):
"""
Delete files that were linked from the given note but no longer are.
@type note: model.Note
@param note: note to search for file links
"""
# load metadata for all files with the given note's note_id
files = self.__database.select_many( File, File.sql_load_note_files( note.object_id ) )
files_to_delete = dict( [ ( db_file.object_id, db_file ) for db_file in files ] )
# search through the note's contents for current links to files
for match in self.FILE_LINK_PATTERN.finditer( note.contents ):
file_id = match.groups( 0 )[ 0 ]
# we've found a link for file_id, so don't delete that file
files_to_delete.pop( file_id, None )
# for each file to delete, delete its metadata from the database and its data from the
# filesystem
for ( file_id, db_file ) in files_to_delete.items():
self.__database.execute( db_file.sql_delete(), commit = False )
os.remove( Upload_file.make_server_filename( file_id ) )
self.__database.commit()

View File

@ -36,7 +36,7 @@ class Notebooks( object ):
"""
Controller for dealing with notebooks and their notes, corresponding to the "/notebooks" URL.
"""
def __init__( self, database, users ):
def __init__( self, database, users, files ):
"""
Create a new Notebooks object.
@ -44,11 +44,14 @@ class Notebooks( object ):
@param database: database that notebooks are stored in
@type users: controller.Users
@param users: controller for all users, used here for updating storage utilization
@rtype: Notebooks
@type files: controller.Files
@param files: controller for all uploaded files, used here for deleting files that are no longer
referenced within saved notes
@return: newly constructed Notebooks
"""
self.__database = database
self.__users = users
self.__files = files
@expose( view = Main_page )
@strongly_expire
@ -509,6 +512,8 @@ class Notebooks( object ):
new_revision = User_revision( note.revision, note.user_id, user.username )
self.__files.purge_unused( note )
return new_revision
# if the note is already in the given notebook, load it and update it

View File

@ -43,8 +43,8 @@ class Root( object ):
settings[ u"global" ].get( u"luminotes.payment_email", u"" ),
settings[ u"global" ].get( u"luminotes.rate_plans", [] ),
)
self.__notebooks = Notebooks( database, self.__users )
self.__files = Files( database, self.__users )
self.__notebooks = Notebooks( database, self.__users, self.__files )
self.__suppress_exceptions = suppress_exceptions # used for unit tests
@expose( Main_page )

View File

@ -98,6 +98,18 @@ class File( Persistent ):
def sql_delete( self ):
return "delete from file where id = %s;" % quote( self.object_id )
@staticmethod
def sql_load_note_files( note_id ):
return \
"""
select
file.id, file.revision, file.notebook_id, file.note_id, file.filename, file.size_bytes, file.content_type
from
file
where
file.note_id = %s;
""" % quote( note_id )
def to_dict( self ):
d = Persistent.to_dict( self )
d.update( dict(

View File

@ -8,3 +8,4 @@ create table file (
content_type text
);
alter table file add primary key ( id );
create index file_note_id_index on file using btree ( note_id );

View File

@ -234,6 +234,13 @@ 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: luminotes_user_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
--