details: http://www.bx.psu.edu/hg/galaxy/rev/4743015d9a57 changeset: 3194:4743015d9a57 user: Greg Von Kuster <greg@bx.psu.edu> date: Wed Dec 23 20:26:01 2009 -0500 description: Data library cleanup - move / merge all duplicate methods from the library_and library_admin controllers into the library_common controller - move / merge all duplicate mako templates into the ~/library/common directory - make the admin view library browser a grid - all library item ids are now encoded diffstat: lib/galaxy/model/__init__.py | 14 + lib/galaxy/tools/actions/upload_common.py | 2 +- lib/galaxy/web/controllers/library.py | 944 ---------- lib/galaxy/web/controllers/library_admin.py | 1004 +---------- lib/galaxy/web/controllers/library_common.py | 1119 ++++++++++++- lib/galaxy/web/controllers/tool_runner.py | 2 +- templates/admin/library/browse_libraries.mako | 51 - templates/admin/library/browse_library.mako | 325 --- templates/admin/library/common.mako | 59 - templates/admin/library/folder_info.mako | 39 - templates/admin/library/folder_permissions.mako | 22 - templates/admin/library/grid.mako | 1 + templates/admin/library/ldda_edit_info.mako | 135 - templates/admin/library/ldda_info.mako | 110 - templates/admin/library/ldda_permissions.mako | 64 - templates/admin/library/library_dataset_info.mako | 50 - templates/admin/library/library_dataset_permissions.mako | 28 - templates/admin/library/library_info.mako | 50 - templates/admin/library/library_permissions.mako | 21 - templates/admin/library/new_folder.mako | 44 - templates/admin/library/new_library.mako | 2 +- templates/admin/library/upload.mako | 50 - templates/admin/requests/show_request.mako | 2 +- templates/base_panels.mako | 4 +- templates/library/browse_libraries.mako | 2 +- templates/library/browse_library.mako | 388 ---- templates/library/common.mako | 59 - templates/library/common/browse_library.mako | 441 +++++ templates/library/common/common.mako | 370 ++++ templates/library/common/folder_info.mako | 56 + templates/library/common/folder_permissions.mako | 20 + templates/library/common/ldda_edit_info.mako | 171 + templates/library/common/ldda_info.mako | 114 + templates/library/common/ldda_permissions.mako | 65 + templates/library/common/library_dataset_info.mako | 71 + templates/library/common/library_dataset_permissions.mako | 34 + templates/library/common/library_info.mako | 61 + templates/library/common/library_item_info.mako | 15 + templates/library/common/library_permissions.mako | 28 + templates/library/common/new_folder.mako | 44 + templates/library/common/select_info_template.mako | 44 + templates/library/common/upload.mako | 56 + templates/library/folder_info.mako | 53 - templates/library/folder_permissions.mako | 20 - templates/library/ldda_edit_info.mako | 168 - templates/library/ldda_info.mako | 112 - templates/library/ldda_permissions.mako | 64 - templates/library/library_dataset_common.mako | 265 --- templates/library/library_dataset_info.mako | 68 - templates/library/library_dataset_permissions.mako | 31 - templates/library/library_info.mako | 64 - templates/library/library_item_info.mako | 15 - templates/library/library_permissions.mako | 25 - templates/library/new_folder.mako | 44 - templates/library/select_info_template.mako | 44 - templates/library/upload.mako | 47 - templates/mobile/manage_library.mako | 16 +- templates/requests/show_request.mako | 9 +- test/base/twilltestcase.py | 109 +- test/functional/test_forms_and_requests.py | 14 +- test/functional/test_security_and_libraries.py | 416 +++- 61 files changed, 3147 insertions(+), 4618 deletions(-) diffs (truncated from 9178 to 3000 lines): diff -r 9f966847abda -r 4743015d9a57 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py Tue Dec 22 18:49:34 2009 -0500 +++ b/lib/galaxy/model/__init__.py Wed Dec 23 20:26:01 2009 -0500 @@ -746,6 +746,13 @@ return template.get_widgets( trans.user, contents=info.content ) return template.get_widgets( trans.user ) return [] + def get_display_name( self ): + # Library name can be either a string or a unicode object. If string, + # convert to unicode object assuming 'utf-8' format. + name = self.name + if isinstance( name, str ): + name = unicode( name, 'utf-8' ) + return name class LibraryFolder( object ): def __init__( self, name=None, description=None, item_count=0, order_id=None ): @@ -811,6 +818,13 @@ def active_datasets( self ): # This needs to be a list return [ ld.library_dataset_dataset_association.dataset for ld in self.datasets if not ld.library_dataset_dataset_association.deleted ] + def get_display_name( self ): + # Library folder name can be either a string or a unicode object. If string, + # convert to unicode object assuming 'utf-8' format. + name = self.name + if isinstance( name, str ): + name = unicode( name, 'utf-8' ) + return name class LibraryDataset( object ): # This class acts as a proxy to the currently selected LDDA diff -r 9f966847abda -r 4743015d9a57 lib/galaxy/tools/actions/upload_common.py --- a/lib/galaxy/tools/actions/upload_common.py Tue Dec 22 18:49:34 2009 -0500 +++ b/lib/galaxy/tools/actions/upload_common.py Wed Dec 23 20:26:01 2009 -0500 @@ -41,7 +41,7 @@ # See if we have any template field contents library_bunch.template_field_contents = [] template_id = params.get( 'template_id', None ) - library_bunch.folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder_id ) + library_bunch.folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) ) # We are inheriting the folder's info_association, so we did not # receive any inherited contents, but we may have redirected here # after the user entered template contents ( due to errors ). diff -r 9f966847abda -r 4743015d9a57 lib/galaxy/web/controllers/library.py --- a/lib/galaxy/web/controllers/library.py Tue Dec 22 18:49:34 2009 -0500 +++ b/lib/galaxy/web/controllers/library.py Wed Dec 23 20:26:01 2009 -0500 @@ -3,50 +3,9 @@ from galaxy.datatypes import sniff from galaxy import util from galaxy.util.odict import odict -from galaxy.util.streamball import StreamBall -import logging, tempfile, zipfile, tarfile, os, sys - -if sys.version_info[:2] < ( 2, 6 ): - zipfile.BadZipFile = zipfile.error -if sys.version_info[:2] < ( 2, 5 ): - zipfile.LargeZipFile = zipfile.error log = logging.getLogger( __name__ ) -# Test for available compression types -tmpd = tempfile.mkdtemp() -comptypes = [] -for comptype in ( 'gz', 'bz2' ): - tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype ) - try: - archive = tarfile.open( tmpf, 'w:' + comptype ) - archive.close() - comptypes.append( comptype ) - except tarfile.CompressionError: - log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype ) - try: - os.unlink( tmpf ) - except OSError: - pass -ziptype = '32' -tmpf = os.path.join( tmpd, 'compression_test.zip' ) -try: - archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True ) - archive.close() - comptypes.append( 'zip' ) - ziptype = '64' -except RuntimeError: - log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." ) -except (TypeError, zipfile.LargeZipFile): - # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped - log.warning( 'Max zip file size is 2GB, ZIP64 not supported' ) - comptypes.append( 'zip' ) -try: - os.unlink( tmpf ) -except OSError: - pass -os.rmdir( tmpd ) - class Library( BaseController ): @web.expose def index( self, trans, **kwd ): @@ -89,906 +48,3 @@ default_action=params.get( 'default_action', None ), msg=msg, messagetype=messagetype ) - @web.expose - def browse_library( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - library_id = params.get( 'obj_id', None ) - if not library_id: - # To handle bots - msg = "You must specify a library id." - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_libraries', - default_action=params.get( 'default_action', None ), - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - library = trans.sa_session.query( trans.app.model.Library ).get( library_id ) - if not library: - # To handle bots - msg = "Invalid library id ( %s )." % str( library_id ) - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_libraries', - default_action=params.get( 'default_action', None ), - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - created_ldda_ids = params.get( 'created_ldda_ids', '' ) - hidden_folder_ids = util.listify( util.restore_text( params.get( 'hidden_folder_ids', '' ) ) ) - if created_ldda_ids and not msg: - msg = "%d datasets are now uploading in the background to the library '%s' ( each is selected ). " % ( len( created_ldda_ids.split(',') ), library.name ) - msg += "Do not navigate away from Galaxy or use the browser's \"stop\" or \"reload\" buttons ( on this tab ) until the upload(s) change from the \"uploading\" state." - messagetype = "info" - return trans.fill_template( '/library/browse_library.mako', - library=library, - created_ldda_ids=created_ldda_ids, - hidden_folder_ids=hidden_folder_ids, - default_action=params.get( 'default_action', None ), - comptypes=comptypes, - msg=msg, - messagetype=messagetype ) - @web.expose - def library( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - library_id = params.get( 'obj_id', None ) - # TODO: eventually we'll want the ability for users to create libraries - if params.get( 'delete', False ): - action = 'delete' - elif params.get( 'permissions', False ): - action = 'permissions' - else: - action = 'information' - if not library_id: - msg = "You must specify a library." - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_libraries', - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - library = trans.sa_session.query( trans.app.model.Library ).get( int( library_id ) ) - if not library: - msg = "Invalid library id ( %s ) specified." % str( obj_id ) - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_libraries', - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if action == 'information': - # See if we have any associated templates - widgets = library.get_template_widgets( trans ) - if params.get( 'rename_library_button', False ): - old_name = library.name - new_name = util.restore_text( params.name ) - new_description = util.restore_text( params.description ) - if not new_name: - msg = 'Enter a valid name' - return trans.fill_template( '/library/library_info.mako', - library=library, - widgets=widgets, - msg=msg, - messagetype='error' ) - else: - library.name = new_name - library.description = new_description - # Rename the root_folder - library.root_folder.name = new_name - library.root_folder.description = new_description - trans.sa_session.add_all( ( library, library.root_folder ) ) - trans.sa_session.flush() - msg = "Library '%s' has been renamed to '%s'" % ( old_name, new_name ) - return trans.response.send_redirect( web.url_for( controller='library', - action='library', - obj_id=library.id, - edit_info=True, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - return trans.fill_template( '/library/library_info.mako', - library=library, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - elif action == 'permissions': - if params.get( 'update_roles_button', False ): - # The user clicked the Save button on the 'Associate With Roles' form - permissions = {} - for k, v in trans.app.model.Library.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - trans.app.security_agent.set_all_library_permissions( library, permissions ) - trans.sa_session.refresh( library ) - # Copy the permissions to the root folder - trans.app.security_agent.copy_library_permissions( library, library.root_folder ) - msg = "Permissions updated for library '%s'" % library.name - return trans.response.send_redirect( web.url_for( controller='library', - action='library', - obj_id=library.id, - permissions=True, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - return trans.fill_template( '/library/library_permissions.mako', - library=library, - msg=msg, - messagetype=messagetype ) - @web.expose - def folder( self, trans, obj_id, library_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - if params.get( 'new', False ): - action = 'new' - elif params.get( 'delete', False ): - action = 'delete' - elif params.get( 'permissions', False ): - action = 'permissions' - else: - # 'information' will be the default - action = 'information' - folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( int( obj_id ) ) - if not folder: - msg = "Invalid folder specified, id: %s" % str( obj_id ) - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - user, roles = trans.get_user_and_roles() - if action == 'new': - if params.new == 'submitted': - new_folder = trans.app.model.LibraryFolder( name=util.restore_text( params.name ), - description=util.restore_text( params.description ) ) - # We are associating the last used genome build with folders, so we will always - # initialize a new folder with the first dbkey in util.dbnames which is currently - # ? unspecified (?) - new_folder.genome_build = util.dbnames.default_value - folder.add_folder( new_folder ) - trans.sa_session.add( new_folder ) - trans.sa_session.flush() - # New folders default to having the same permissions as their parent folder - trans.app.security_agent.copy_library_permissions( folder, new_folder ) - msg = "New folder named '%s' has been added to the library" % new_folder.name - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - return trans.fill_template( '/library/new_folder.mako', - library_id=library_id, - folder=folder, - msg=msg, - messagetype=messagetype ) - elif action == 'information': - # See if we have any associated templates - widgets = folder.get_template_widgets( trans ) - if params.get( 'rename_folder_button', False ): - if trans.app.security_agent.can_modify_library_item( user, roles, folder ): - old_name = folder.name - new_name = util.restore_text( params.name ) - new_description = util.restore_text( params.description ) - if not new_name: - msg = 'Enter a valid name' - return trans.fill_template( "/library/folder_info.mako", - folder=folder, - library_id=library_id, - widgets=widgets, - msg=msg, - messagetype='error' ) - else: - folder.name = new_name - folder.description = new_description - trans.sa_session.add( folder ) - trans.sa_session.flush() - msg = "Folder '%s' has been renamed to '%s'" % ( old_name, new_name ) - return trans.response.send_redirect( web.url_for( controller='library', - action='folder', - obj_id=folder.id, - library_id=library_id, - rename=True, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - else: - msg = "You are not authorized to edit this folder" - return trans.fill_template( "/library/folder_info.mako", - folder=folder, - library_id=library_id, - widgets=widgets, - msg=msg, - messagetype='error' ) - return trans.fill_template( '/library/folder_info.mako', - folder=folder, - library_id=library_id, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - elif action == 'permissions': - if params.get( 'update_roles_button', False ): - # The user clicked the Save button on the 'Associate With Roles' form - if trans.app.security_agent.can_manage_library_item( user, roles, folder ): - permissions = {} - for k, v in trans.app.model.Library.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( params.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - trans.app.security_agent.set_all_library_permissions( folder, permissions ) - trans.sa_session.refresh( folder ) - msg = 'Permissions updated for folder %s' % folder.name - return trans.response.send_redirect( web.url_for( controller='library', - action='folder', - obj_id=folder.id, - library_id=library_id, - permissions=True, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - else: - msg = "You are not authorized to manage permissions on this folder" - return trans.response.send_redirect( web.url_for( controller='library', - action='folder', - obj_id=folder.id, - library_id=library_id, - permissions=True, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - return trans.fill_template( '/library/folder_permissions.mako', - folder=folder, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - @web.expose - def library_dataset( self, trans, obj_id, library_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - if params.get( 'permissions', False ): - action = 'permissions' - else: - action = 'information' - library_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( obj_id ) - if not library_dataset: - msg = "Invalid library dataset specified, id: %s" %str( obj_id ) - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - user, roles = trans.get_user_and_roles() - if action == 'information': - if params.get( 'edit_attributes_button', False ): - if trans.app.security_agent.can_modify_library_item( user, roles, library_dataset ): - if params.get( 'edit_attributes_button', False ): - old_name = library_dataset.name - new_name = util.restore_text( params.get( 'name', '' ) ) - new_info = util.restore_text( params.get( 'info', '' ) ) - if not new_name: - msg = 'Enter a valid name' - messagetype = 'error' - else: - library_dataset.name = new_name - library_dataset.info = new_info - trans.sa_session.add( library_dataset ) - trans.sa_session.flush() - msg = "Dataset '%s' has been renamed to '%s'" % ( old_name, new_name ) - messagetype = 'done' - else: - msg = "You are not authorized to change the attributes of this dataset" - messagetype = "error" - return trans.fill_template( '/library/library_dataset_info.mako', - library_dataset=library_dataset, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - elif action == 'permissions': - if params.get( 'update_roles_button', False ): - if trans.app.security_agent.can_manage_library_item( user, roles, library_dataset ): - # The user clicked the Save button on the 'Associate With Roles' form - permissions = {} - for k, v in trans.app.model.Library.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - # Set the LIBRARY permissions on the LibraryDataset - # NOTE: the LibraryDataset and LibraryDatasetDatasetAssociation will be set with the same permissions - trans.app.security_agent.set_all_library_permissions( library_dataset, permissions ) - trans.sa_session.refresh( library_dataset ) - # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation - trans.app.security_agent.set_all_library_permissions( library_dataset.library_dataset_dataset_association, permissions ) - trans.sa_session.refresh( library_dataset.library_dataset_dataset_association ) - msg = 'Permissions and roles have been updated for library dataset %s' % library_dataset.name - messagetype = 'done' - else: - msg = "You are not authorized to managed the permissions of this dataset" - messagetype = "error" - return trans.fill_template( '/library/library_dataset_permissions.mako', - library_dataset=library_dataset, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - @web.expose - def ldda_edit_info( self, trans, library_id, folder_id, obj_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( obj_id ) - if not ldda: - msg = "Invalid LibraryDatasetDatasetAssociation specified, obj_id: %s" % str( obj_id ) - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - dbkey = params.get( 'dbkey', '?' ) - if isinstance( dbkey, list ): - dbkey = dbkey[0] - user, roles = trans.get_user_and_roles() - file_formats = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ] - file_formats.sort() - # See if we have any associated templates - widgets = ldda.get_template_widgets( trans ) - if params.get( 'change', False ): - # The user clicked the Save button on the 'Change data type' form - if trans.app.security_agent.can_modify_library_item( user, roles, ldda ): - if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( params.datatype ).allow_datatype_change: - trans.app.datatypes_registry.change_datatype( ldda, params.datatype ) - trans.sa_session.flush() - msg = "Data type changed for library dataset '%s'" % ldda.name - messagetype = 'done' - else: - msg = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( ldda.extension, params.datatype ) - messagetype = 'error' - else: - msg = "You are not authorized to change the data type of dataset '%s'" % ldda.name - messagetype = 'error' - return trans.fill_template( "/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - elif params.get( 'save', False ): - # The user clicked the Save button on the 'Edit Attributes' form - if trans.app.security_agent.can_modify_library_item( user, roles, ldda ): - old_name = ldda.name - new_name = util.restore_text( params.get( 'name', '' ) ) - new_info = util.restore_text( params.get( 'info', '' ) ) - new_message = util.restore_text( params.get( 'message', '' ) ) - if not new_name: - msg = 'Enter a valid name' - messagetype = 'error' - else: - ldda.name = new_name - ldda.info = new_info - ldda.message = new_message - # The following for loop will save all metadata_spec items - for name, spec in ldda.datatype.metadata_spec.items(): - if spec.get("readonly"): - continue - optional = params.get( "is_" + name, None ) - if optional and optional == 'true': - # optional element... == 'true' actually means it is NOT checked (and therefore ommitted) - setattr( ldda.metadata, name, None ) - else: - setattr( ldda.metadata, name, spec.unwrap( params.get ( name, None ) ) ) - ldda.metadata.dbkey = dbkey - ldda.datatype.after_setting_metadata( ldda ) - trans.sa_session.flush() - msg = 'Attributes updated for library dataset %s' % ldda.name - messagetype = 'done' - else: - msg = "you are not authorized to edit the attributes of dataset '%s'" % ldda.name - messagetype = 'error' - return trans.fill_template( "/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - elif params.get( 'detect', False ): - # The user clicked the Auto-detect button on the 'Edit Attributes' form - if trans.app.security_agent.can_modify_library_item( user, roles, ldda ): - for name, spec in ldda.datatype.metadata_spec.items(): - # We need to be careful about the attributes we are resetting - if name not in [ 'name', 'info', 'dbkey' ]: - if spec.get( 'default' ): - setattr( ldda.metadata, name, spec.unwrap( spec.get( 'default' ) ) ) - ldda.datatype.set_meta( ldda ) - ldda.datatype.after_setting_metadata( ldda ) - trans.sa_session.flush() - msg = 'Attributes updated for library dataset %s' % ldda.name - messagetype = 'done' - else: - msg = "you are not authorized to edit the attributes of dataset '%s'" % ldda.name - messagetype = 'error' - return trans.fill_template( "/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - elif params.get( 'delete', False ): - if trans.app.security_agent.can_modify_library_item( user, roles, folder ): - ldda.deleted = True - trans.sa_session.add( ldda ) - trans.sa_session.flush() - msg = 'Dataset %s has been removed from this data library' % ldda.name - messagetype = 'done' - else: - msg = "you are not authorized to delete dataset '%s'" % ldda.name - messagetype = 'error' - return trans.fill_template( "/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - if trans.app.security_agent.can_modify_library_item( user, roles, ldda ): - if "dbkey" in ldda.datatype.metadata_spec and not ldda.metadata.dbkey: - # Copy dbkey into metadata, for backwards compatability - # This looks like it does nothing, but getting the dbkey - # returns the metadata dbkey unless it is None, in which - # case it resorts to the old dbkey. Setting the dbkey - # sets it properly in the metadata - ldda.metadata.dbkey = ldda.dbkey - return trans.fill_template( "/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - @web.expose - def ldda_display_info( self, trans, library_id, folder_id, obj_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( obj_id ) - if not ldda: - msg = "Invalid LibraryDatasetDatasetAssociation specified, id: %s" % str( obj_id ) - return trans.response.send_redirect( web.url_for( controller='admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - # See if we have any associated templates - widgets = ldda.get_template_widgets( trans ) - return trans.fill_template( '/library/ldda_info.mako', - ldda=ldda, - library_id=library_id, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - @web.expose - def ldda_manage_permissions( self, trans, library_id, folder_id, obj_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - obj_ids = util.listify( obj_id ) - # Display permission form, permissions will be updated for all lddas simultaneously. - lddas = [] - for obj_id in [ int( obj_id ) for obj_id in obj_ids ]: - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( obj_id ) - if ldda is None: - msg = 'You specified an invalid LibraryDatasetDatasetAssociation id: %s' %str( obj_id ) - trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - lddas.append( ldda ) - if params.get( 'update_roles_button', False ): - if trans.app.security_agent.can_manage_library_item( user, roles, ldda ) and \ - trans.app.security_agent.can_manage_dataset( roles, ldda.dataset ): - permissions = {} - for k, v in trans.app.model.Dataset.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - for ldda in lddas: - # Set the DATASET permissions on the Dataset - trans.app.security_agent.set_all_dataset_permissions( ldda.dataset, permissions ) - trans.sa_session.refresh( ldda.dataset ) - permissions = {} - for k, v in trans.app.model.Library.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - for ldda in lddas: - # Set the LIBRARY permissions on the LibraryDataset - # NOTE: the LibraryDataset and LibraryDatasetDatasetAssociation will be set with the same permissions - trans.app.security_agent.set_all_library_permissions( ldda.library_dataset, permissions ) - trans.sa_session.refresh( ldda.library_dataset ) - # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation - trans.app.security_agent.set_all_library_permissions( ldda, permissions ) - trans.sa_session.refresh( ldda ) - msg = 'Permissions and roles have been updated on %d datasets' % len( lddas ) - messagetype = 'done' - else: - msg = "You are not authorized to change the permissions of dataset '%s'" % ldda.name - messagetype = 'error' - return trans.fill_template( "/library/ldda_permissions.mako", - ldda=lddas, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - if trans.app.security_agent.can_manage_library_item( user, roles, ldda ) and \ - trans.app.security_agent.can_manage_dataset( roles, ldda.dataset ): - # Ensure that the permissions across all library items are identical, otherwise we can't update them together. - check_list = [] - for ldda in lddas: - permissions = [] - # Check the library level permissions - the permissions on the LibraryDatasetDatasetAssociation - # will always be the same as the permissions on the associated LibraryDataset, so we only need to - # check one Library object - for library_permission in trans.app.security_agent.get_library_dataset_permissions( ldda.library_dataset ): - if library_permission.action not in permissions: - permissions.append( library_permission.action ) - for dataset_permission in trans.app.security_agent.get_dataset_permissions( ldda.dataset ): - if dataset_permission.action not in permissions: - permissions.append( dataset_permission.action ) - permissions.sort() - if not check_list: - check_list = permissions - if permissions != check_list: - msg = 'The datasets you selected do not have identical permissions, so they can not be updated together' - trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - else: - msg = "You are not authorized to change the permissions of dataset '%s'" % ldda.name - messagetype = 'error' - return trans.fill_template( "/library/ldda_permissions.mako", - ldda=lddas, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - @web.expose - def upload_library_dataset( self, trans, library_id, folder_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - dbkey = params.get( 'dbkey', None ) - if isinstance( dbkey, list ): - last_used_build = dbkey[0] - else: - last_used_build = dbkey - folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder_id ) - if folder and last_used_build in [ 'None', None, '?' ]: - last_used_build = folder.genome_build - replace_id = params.get( 'replace_id', None ) - if replace_id not in [ None, 'None' ]: - replace_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( replace_id ) - if not last_used_build: - last_used_build = replace_dataset.library_dataset_dataset_association.dbkey - # Don't allow multiple datasets to be uploaded when replacing a dataset with a new version - upload_option = 'upload_file' - else: - replace_dataset = None - upload_option = params.get( 'upload_option', 'upload_file' ) - user, roles = trans.get_user_and_roles() - if trans.app.security_agent.can_add_library_item( user, roles, folder ) or \ - ( replace_dataset and trans.app.security_agent.can_modify_library_item( user, roles, replace_dataset ) ): - if params.get( 'runtool_btn', False ) or params.get( 'ajax_upload', False ): - # See if we have any inherited templates, but do not inherit contents. - info_association, inherited = folder.get_info_association( inherited=True ) - if info_association: - template_id = str( info_association.template.id ) - widgets = folder.get_template_widgets( trans, get_contents=False ) - else: - template_id = 'None' - widgets = [] - created_outputs = trans.webapp.controllers[ 'library_common' ].upload_dataset( trans, - controller='library', - library_id=library_id, - folder_id=folder_id, - template_id=template_id, - widgets=widgets, - replace_dataset=replace_dataset, - **kwd ) - if created_outputs: - ldda_id_list = [ str( v.id ) for v in created_outputs.values() ] - total_added = len( created_outputs.values() ) - if replace_dataset: - msg = "Added %d dataset versions to the library dataset '%s' in the folder '%s'." % ( total_added, replace_dataset.name, folder.name ) - else: - if not folder.parent: - # Libraries have the same name as their root_folder - msg = "Added %d datasets to the library '%s' ( each is selected ). " % ( total_added, folder.name ) - else: - msg = "Added %d datasets to the folder '%s' ( each is selected ). " % ( total_added, folder.name ) - # Since permissions on all LibraryDatasetDatasetAssociations must be the same at this point, we only need - # to check one of them to see if the current user can manage permissions on them. - check_ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( ldda_id_list[0] ) - if trans.app.security_agent.can_manage_library_item( user, roles, check_ldda ): - if replace_dataset: - default_action = '' - else: - msg += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." - default_action = 'manage_permissions' - else: - default_action = 'add' - trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - default_action=default_action, - created_ldda_ids=",".join( ldda_id_list ), - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - - else: - msg = "Upload failed" - trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - created_ldda_ids=",".join( ldda_id_list ), - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - # See if we have any inherited templates, but do not inherit contents. - widgets = folder.get_template_widgets( trans, get_contents=False ) - upload_option = params.get( 'upload_option', 'upload_file' ) - # No dataset(s) specified, so display the upload form. Send list of data formats to the form - # so the "extension" select list can be populated dynamically - file_formats = trans.app.datatypes_registry.upload_file_formats - # Send list of genome builds to the form so the "dbkey" select list can be populated dynamically - def get_dbkey_options( last_used_build ): - for dbkey, build_name in util.dbnames: - yield build_name, dbkey, ( dbkey==last_used_build ) - dbkeys = get_dbkey_options( last_used_build ) - # Send list of roles to the form so the dataset can be associated with 1 or more of them. - roles = trans.sa_session.query( trans.app.model.Role ) \ - .filter( trans.app.model.Role.table.c.deleted==False ) \ - .order_by( trans.app.model.Role.table.c.name ) - # Send the current history to the form to enable importing datasets from history to library - history = trans.get_history() - trans.sa_session.refresh( history ) - # If we're using nginx upload, override the form action - action = web.url_for( controller='library', action='upload_library_dataset' ) - if upload_option == 'upload_file' and trans.app.config.nginx_upload_path: - action = web.url_for( trans.app.config.nginx_upload_path ) + '?nginx_redir=' + action - return trans.fill_template( '/library/upload.mako', - upload_option=upload_option, - action=action, - library_id=library_id, - folder_id=folder_id, - replace_dataset=replace_dataset, - file_formats=file_formats, - dbkeys=dbkeys, - last_used_build=last_used_build, - roles=roles, - history=history, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - @web.expose - def add_history_datasets_to_library( self, trans, library_id, folder_id, hda_ids='', **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - try: - folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( int( folder_id ) ) - except: - msg = "Invalid folder id: %s" % str( folder_id ) - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - replace_id = params.get( 'replace_id', None ) - if replace_id: - replace_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( replace_id ) - else: - replace_dataset = None - # See if the current history is empty - history = trans.get_history() - trans.sa_session.refresh( history ) - if not history.active_datasets: - msg = 'Your current history is empty' - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if params.get( 'add_history_datasets_to_library_button', False ): - hda_ids = util.listify( hda_ids ) - if hda_ids: - dataset_names = [] - created_ldda_ids = '' - for hda_id in hda_ids: - hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( hda_id ) - if hda: - ldda = hda.to_library_dataset_dataset_association( target_folder=folder, replace_dataset=replace_dataset ) - created_ldda_ids = '%s,%s' % ( created_ldda_ids, str( ldda.id ) ) - dataset_names.append( ldda.name ) - if not replace_dataset: - # If replace_dataset is None, the Library level permissions will be taken from the folder and applied to the new - # LDDA and LibraryDataset. - trans.app.security_agent.copy_library_permissions( folder, ldda ) - trans.app.security_agent.copy_library_permissions( folder, ldda.library_dataset ) - # Permissions must be the same on the LibraryDatasetDatasetAssociation and the associated LibraryDataset - trans.app.security_agent.copy_library_permissions( ldda.library_dataset, ldda ) - else: - msg = "The requested HistoryDatasetAssociation id %s is invalid" % str( hda_id ) - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if created_ldda_ids: - created_ldda_ids = created_ldda_ids.lstrip( ',' ) - ldda_id_list = created_ldda_ids.split( ',' ) - total_added = len( ldda_id_list ) - if replace_dataset: - msg = "Added %d dataset versions to the library dataset '%s' in the folder '%s'." % ( total_added, replace_dataset.name, folder.name ) - else: - if not folder.parent: - # Libraries have the same name as their root_folder - msg = "Added %d datasets to the library '%s' ( each is selected ). " % ( total_added, folder.name ) - else: - msg = "Added %d datasets to the folder '%s' ( each is selected ). " % ( total_added, folder.name ) - # Since permissions on all LibraryDatasetDatasetAssociations must be the same at this point, we only need - # to check one of them to see if the current user can manage permissions on them. - check_ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( ldda_id_list[0] ) - user, roles = trans.get_user_and_roles() - if trans.app.security_agent.can_manage_library_item( user, roles, check_ldda ): - if replace_dataset: - default_action = '' - else: - msg += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." - default_action = 'manage_permissions' - else: - default_action = 'add' - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - created_ldda_ids=created_ldda_ids.lstrip( ',' ), - default_action=default_action, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - else: - msg = 'Select at least one dataset from the list of active datasets in your current history' - messagetype = 'error' - last_used_build = folder.genome_build - upload_option = params.get( 'upload_option', 'import_from_history' ) - # Send list of data formats to the form so the "extension" select list can be populated dynamically - file_formats = trans.app.datatypes_registry.upload_file_formats - # Send list of genome builds to the form so the "dbkey" select list can be populated dynamically - def get_dbkey_options( last_used_build ): - for dbkey, build_name in util.dbnames: - yield build_name, dbkey, ( dbkey==last_used_build ) - dbkeys = get_dbkey_options( last_used_build ) - # Send list of roles to the form so the dataset can be associated with 1 or more of them. - roles = trans.sa_session.query( trans.app.model.Role ) \ - .filter( trans.app.model.Role.table.c.deleted==False ) \ - .order_by( trans.app.model.Role.table.c.name ) - return trans.fill_template( "/library/upload.mako", - upload_option=upload_option, - library_id=library_id, - folder_id=folder_id, - replace_dataset=replace_dataset, - file_formats=file_formats, - dbkeys=dbkeys, - last_used_build=last_used_build, - roles=roles, - history=history, - widgets=[], - msg=msg, - messagetype=messagetype ) - @web.expose - def datasets( self, trans, library_id, ldda_ids='', **kwd ): - # This method is used by the select list labeled "Perform action on selected datasets" - # on the analysis library browser. - if not ldda_ids: - msg = "You must select at least one dataset" - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - ldda_ids = util.listify( ldda_ids ) - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - if not params.do_action: - msg = "You must select an action to perform on selected datasets" - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if params.do_action == 'add': - history = trans.get_history() - for ldda_id in ldda_ids: - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( ldda_id ) - hda = ldda.to_history_dataset_association( target_history=history, add_to_history = True ) - trans.sa_session.add( history ) - trans.sa_session.flush() - msg = "%i dataset(s) have been imported into your history" % len( ldda_ids ) - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - elif params.do_action == 'manage_permissions': - # We need the folder containing the LibraryDatasetDatasetAssociation(s) - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( ldda_ids[0] ) - trans.response.send_redirect( web.url_for( controller='library', - action='upload_library_dataset', - library_id=library_id, - folder_id=ldda.library_dataset.folder.id, - obj_id=','.join( ldda_ids ), - permissions=True, - msg=util.sanitize_text( msg ), - messagetype=messagetype ) ) - else: - try: - if params.do_action == 'zip': - # Can't use mkstemp - the file must not exist first - tmpd = tempfile.mkdtemp() - tmpf = os.path.join( tmpd, 'library_download.' + params.do_action ) - if ziptype == '64': - archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True ) - else: - archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED ) - archive.add = lambda x, y: archive.write( x, y.encode('CP437') ) - elif params.do_action == 'tgz': - archive = util.streamball.StreamBall( 'w|gz' ) - elif params.do_action == 'tbz': - archive = util.streamball.StreamBall( 'w|bz2' ) - except (OSError, zipfile.BadZipFile): - log.exception( "Unable to create archive for download" ) - msg = "Unable to create archive for download, please report this error" - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - seen = [] - user, roles = trans.get_user_and_roles() - for ldda_id in ldda_ids: - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( ldda_id ) - if not ldda or not trans.app.security_agent.can_access_dataset( roles, ldda.dataset ): - continue - path = "" - parent_folder = ldda.library_dataset.folder - while parent_folder is not None: - # Exclude the now-hidden "root folder" - if parent_folder.parent is None: - path = os.path.join( parent_folder.library_root[0].name, path ) - break - path = os.path.join( parent_folder.name, path ) - parent_folder = parent_folder.parent - path += ldda.name - while path in seen: - path += '_' - seen.append( path ) - try: - archive.add( ldda.dataset.file_name, path ) - except IOError: - log.exception( "Unable to write to temporary library download archive" ) - msg = "Unable to create archive for download, please report this error" - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if params.do_action == 'zip': - archive.close() - tmpfh = open( tmpf ) - # clean up now - try: - os.unlink( tmpf ) - os.rmdir( tmpd ) - except OSError: - log.exception( "Unable to remove temporary library download archive and directory" ) - msg = "Unable to create archive for download, please report this error" - return trans.response.send_redirect( web.url_for( controller='library', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - trans.response.set_content_type( "application/x-zip-compressed" ) - trans.response.headers[ "Content-Disposition" ] = "attachment; filename=GalaxyLibraryFiles.%s" % params.do_action - return tmpfh - else: - trans.response.set_content_type( "application/x-tar" ) - trans.response.headers[ "Content-Disposition" ] = "attachment; filename=GalaxyLibraryFiles.%s" % params.do_action - archive.wsgi_status = trans.response.wsgi_status() - archive.wsgi_headeritems = trans.response.wsgi_headeritems() - return archive.stream diff -r 9f966847abda -r 4743015d9a57 lib/galaxy/web/controllers/library_admin.py --- a/lib/galaxy/web/controllers/library_admin.py Tue Dec 22 18:49:34 2009 -0500 +++ b/lib/galaxy/web/controllers/library_admin.py Wed Dec 23 20:26:01 2009 -0500 @@ -1,6 +1,7 @@ import sys from galaxy import util from galaxy.web.base.controller import * +from galaxy.web.framework.helpers import time_ago, iff, grids from galaxy.model.orm import * # Older py compatibility try: @@ -11,199 +12,123 @@ import logging log = logging.getLogger( __name__ ) +# States for passing messages +SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error" + +class LibraryListGrid( grids.Grid ): + class NameColumn( grids.TextColumn ): + def get_value( self, trans, grid, library ): + return library.name + class DescriptionColumn( grids.TextColumn ): + def get_value( self, trans, grid, library ): + if library.description: + return library.description + return '' + class StatusColumn( grids.GridColumn ): + def get_value( self, trans, grid, library ): + if library.purged: + return "purged" + elif library.deleted: + return "deleted" + return "" + class DeletedColumn( grids.GridColumn ): + def get_accepted_filters( self ): + """ Returns a list of accepted filters for this column. """ + accepted_filter_labels_and_vals = { "active" : "False", "deleted" : "True", "all": "All" } + accepted_filters = [] + for label, val in accepted_filter_labels_and_vals.items(): + args = { self.key: val } + accepted_filters.append( grids.GridColumnFilter( label, args) ) + return accepted_filters + # Grid definition + title = "Data Libraries" + model_class = model.Library + template='/admin/library/grid.mako' + default_sort_key = "name" + columns = [ + NameColumn( "Library Name", + key="name", + model_class=model.Library, + link=( lambda library: dict( operation="browse", id=library.id ) ), + attach_popup=False, + filterable="advanced" ), + DescriptionColumn( "Description", + key="description", + model_class=model.Library, + attach_popup=False, + filterable="advanced" ), + grids.GridColumn( "Created", key="create_time", format=time_ago ), + grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), + StatusColumn( "Status", attach_popup=False ), + # Columns that are valid for filtering but are not visible. + DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + ] + columns.append( grids.MulticolFilterColumn( "Search", + cols_to_filter=[ columns[0], columns[1] ], + key="free-text-search", + visible=False, + filterable="standard" ) ) + global_actions = [ + grids.GridAction( "Create new data library", dict( controller='library_admin', action='create_library' ) ) + ] + standard_filters = [ + grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), + grids.GridColumnFilter( "Deleted", args=dict( deleted=True, purged=False ) ), + grids.GridColumnFilter( "Purged", args=dict( purged=True ) ), + grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + ] + default_filter = dict( name="All", description="All", deleted="False", purged="False" ) + num_rows_per_page = 50 + preserve_state = False + use_paging = True + def build_initial_query( self, session ): + return session.query( self.model_class ) + class LibraryAdmin( BaseController ): + + library_list_grid = LibraryListGrid() + @web.expose @web.require_admin - def browse_libraries( self, trans, **kwd ): + def browse_libraries( self, trans, **kwargs ): + if 'operation' in kwargs: + operation = kwargs['operation'].lower() + if operation == "browse": + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller='library_admin', + **kwargs ) ) + # Render the list view + return self.library_list_grid( trans, **kwargs ) + @web.expose + @web.require_admin + def create_library( self, trans, **kwd ): params = util.Params( kwd ) msg = util.restore_text( params.get( 'msg', '' ) ) messagetype = params.get( 'messagetype', 'done' ) - return trans.fill_template( '/admin/library/browse_libraries.mako', - libraries=trans.sa_session.query( trans.app.model.Library ) \ - .filter( trans.app.model.Library.table.c.deleted==False ) \ - .order_by( trans.app.model.Library.name ), - deleted=False, - show_deleted=False, - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def browse_library( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - library_id = params.get( 'obj_id', None ) - if not library_id: - # To handle bots - msg = "You must specify a library id." - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_libraries', + if params.get( 'new', False ): + library = trans.app.model.Library( name = util.restore_text( params.name ), + description = util.restore_text( params.description ) ) + root_folder = trans.app.model.LibraryFolder( name = util.restore_text( params.name ), description = "" ) + library.root_folder = root_folder + trans.sa_session.add_all( ( library, root_folder ) ) + trans.sa_session.flush() + msg = "The new library named '%s' has been created" % library.name + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller='library_admin', + id=trans.security.encode_id( library.id ), msg=util.sanitize_text( msg ), - messagetype='error' ) ) - deleted = util.string_as_bool( params.get( 'deleted', False ) ) - show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) - library = trans.sa_session.query( trans.app.model.Library ).get( library_id ) - if not library: - msg = "Invalid library id ( %s )." % str( library_id ) - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_libraries', - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - created_ldda_ids = params.get( 'created_ldda_ids', '' ) - if created_ldda_ids and not msg: - msg = "%d datasets are now uploading in the background to the library '%s' ( each is selected ). " % ( len( created_ldda_ids.split( ',' ) ), library.name ) - msg += "Do not navigate away from Galaxy or use the browser's \"stop\" or \"reload\" buttons ( on this tab ) until the upload(s) change from the \"uploading\" state." - messagetype = "info" - return trans.fill_template( '/admin/library/browse_library.mako', - library=library, - deleted=deleted, - show_deleted=show_deleted, - created_ldda_ids=created_ldda_ids, - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def library( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - library_id = params.get( 'obj_id', None ) - if params.get( 'new', False ): - action = 'new' - elif params.get( 'delete', False ): - action = 'delete' - elif params.get( 'permissions', False ): - action = 'permissions' - else: - action = 'information' - if not library_id and not action == 'new': - msg = "You must specify a library to %s." % action - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_libraries', - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if not action == 'new': - library = trans.sa_session.query( trans.app.model.Library ).get( int( library_id ) ) - if action == 'new': - if params.new == 'submitted': - library = trans.app.model.Library( name = util.restore_text( params.name ), - description = util.restore_text( params.description ) ) - root_folder = trans.app.model.LibraryFolder( name = util.restore_text( params.name ), description = "" ) - library.root_folder = root_folder - trans.sa_session.add_all( ( library, root_folder ) ) - trans.sa_session.flush() - msg = "The new library named '%s' has been created" % library.name - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library.id, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - return trans.fill_template( '/admin/library/new_library.mako', msg=msg, messagetype=messagetype ) - elif action == 'information': - # See if we have any associated templates - widgets = library.get_template_widgets( trans ) - if params.get( 'rename_library_button', False ): - old_name = library.name - new_name = util.restore_text( params.name ) - new_description = util.restore_text( params.description ) - if not new_name: - msg = 'Enter a valid name' - return trans.fill_template( '/admin/library/library_info.mako', - library=library, - widgets=widgets, - msg=msg, - messagetype='error' ) - else: - library.name = new_name - library.description = new_description - # Rename the root_folder - library.root_folder.name = new_name - library.root_folder.description = new_description - trans.sa_session.add_all( ( library, library.root_folder ) ) - trans.sa_session.flush() - msg = "Library '%s' has been renamed to '%s'" % ( old_name, new_name ) - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='library', - obj_id=library.id, - edit_info=True, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - return trans.fill_template( '/admin/library/library_info.mako', - library=library, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - elif action == 'delete': - def delete_folder( library_folder ): - trans.sa_session.refresh( library_folder ) - for folder in library_folder.folders: - delete_folder( folder ) - for library_dataset in library_folder.datasets: - trans.sa_session.refresh( library_dataset ) - ldda = library_dataset.library_dataset_dataset_association - if ldda: - trans.sa_session.refresh( ldda ) - # We don't set ldda.dataset.deleted to True here because the cleanup_dataset script - # will eventually remove it from disk. The purge_library method below sets the dataset - # to deleted. This allows for the library to be undeleted ( before it is purged ), - # restoring all of its contents. - ldda.deleted = True - trans.sa_session.add( ldda ) - library_dataset.deleted = True - trans.sa_session.add( library_dataset ) - library_folder.deleted = True - trans.sa_session.add( library_folder ) - trans.sa_session.flush() - trans.sa_session.refresh( library ) - delete_folder( library.root_folder ) - library.deleted = True - trans.sa_session.add( library ) - trans.sa_session.flush() - msg = "Library '%s' and all of its contents have been marked deleted" % library.name - return trans.response.send_redirect( web.url_for( action='browse_libraries', msg=util.sanitize_text( msg ), messagetype='done' ) ) - elif action == 'permissions': - if params.get( 'update_roles_button', False ): - # The user clicked the Save button on the 'Associate With Roles' form - permissions = {} - for k, v in trans.app.model.Library.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - trans.app.security_agent.set_all_library_permissions( library, permissions ) - trans.sa_session.refresh( library ) - # Copy the permissions to the root folder - trans.app.security_agent.copy_library_permissions( library, library.root_folder ) - msg = "Permissions updated for library '%s'" % library.name - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='library', - obj_id=library.id, - permissions=True, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - return trans.fill_template( '/admin/library/library_permissions.mako', - library=library, - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def deleted_libraries( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - libraries = trans.sa_session.query( trans.app.model.Library ) \ - .filter( and_( trans.app.model.Library.table.c.deleted==True, - trans.app.model.Library.table.c.purged==False ) ) \ - .order_by( trans.app.model.Library.table.c.name ) - return trans.fill_template( '/admin/library/browse_libraries.mako', - libraries=libraries, - deleted=True, - msg=msg, - messagetype=messagetype, - show_deleted=True ) + messagetype='done' ) ) + return trans.fill_template( '/admin/library/new_library.mako', msg=msg, messagetype=messagetype ) @web.expose @web.require_admin def purge_library( self, trans, **kwd ): + # TODO: change this function to purge_library_item, behaving similar to delete_library_item + # assuming we want the ability to purge libraries. + # This function is currently only used by the functional tests. params = util.Params( kwd ) - library = trans.sa_session.query( trans.app.model.Library ).get( int( params.obj_id ) ) + library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( params.id ) ) def purge_folder( library_folder ): for lf in library_folder.folders: purge_folder( lf ) @@ -232,8 +157,8 @@ msg = "Library '%s' has not been marked deleted, so it cannot be purged" % ( library.name ) return trans.response.send_redirect( web.url_for( controller='library_admin', action='browse_libraries', - msg=util.sanitize_text( msg ), - messagetype='error' ) ) + message=util.sanitize_text( msg ), + status='error' ) ) else: purge_folder( library.root_folder ) library.purged = True @@ -241,680 +166,9 @@ trans.sa_session.flush() msg = "Library '%s' and all of its contents have been purged, datasets will be removed from disk via the cleanup_datasets script" % library.name return trans.response.send_redirect( web.url_for( controller='library_admin', - action='deleted_libraries', - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - @web.expose - @web.require_admin - def folder( self, trans, obj_id, library_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - if params.get( 'new', False ): - action = 'new' - elif params.get( 'delete', False ): - action = 'delete' - elif params.get( 'permissions', False ): - action = 'permissions' - else: - # 'information' will be the default - action = 'information' - folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( int( obj_id ) ) - if not folder: - msg = "Invalid folder specified, id: %s" % str( obj_id ) - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if action == 'new': - if params.new == 'submitted': - new_folder = trans.app.model.LibraryFolder( name=util.restore_text( params.name ), - description=util.restore_text( params.description ) ) - # We are associating the last used genome build with folders, so we will always - # initialize a new folder with the first dbkey in util.dbnames which is currently - # ? unspecified (?) - new_folder.genome_build = util.dbnames.default_value - folder.add_folder( new_folder ) - trans.sa_session.add( new_folder ) - trans.sa_session.flush() - # New folders default to having the same permissions as their parent folder - trans.app.security_agent.copy_library_permissions( folder, new_folder ) - msg = "New folder named '%s' has been added to the library" % new_folder.name - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - return trans.fill_template( '/admin/library/new_folder.mako', - library_id=library_id, - folder=folder, - msg=msg, - messagetype=messagetype ) - elif action == 'information': - # See if we have any associated templates - widgets = folder.get_template_widgets( trans ) - if params.get( 'rename_folder_button', False ): - old_name = folder.name - new_name = util.restore_text( params.name ) - new_description = util.restore_text( params.description ) - if not new_name: - msg = 'Enter a valid name' - return trans.fill_template( '/admin/library/folder_info.mako', - folder=folder, - library_id=library_id, - widgets=widgets, - msg=msg, - messagetype='error' ) - else: - folder.name = new_name - folder.description = new_description - trans.sa_session.add( folder ) - trans.sa_session.flush() - msg = "Folder '%s' has been renamed to '%s'" % ( old_name, new_name ) - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='folder', - obj_id=folder.id, - library_id=library_id, - edit_info=True, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - return trans.fill_template( '/admin/library/folder_info.mako', - folder=folder, - library_id=library_id, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - elif action == 'delete': - folder.deleted = True - trans.sa_session.add( folder ) - trans.sa_session.flush() - msg = "Folder '%s' and all of its contents have been marked deleted" % folder.name - return trans.response.send_redirect( web.url_for( action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - elif action =='permissions': - if params.get( 'update_roles_button', False ): - # The user clicked the Save button on the 'Associate With Roles' form - permissions = {} - for k, v in trans.app.model.Library.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( params.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - trans.app.security_agent.set_all_library_permissions( folder, permissions ) - trans.sa_session.refresh( folder ) - msg = "Permissions updated for folder '%s'" % folder.name - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='folder', - obj_id=folder.id, - library_id=library_id, - permissions=True, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - return trans.fill_template( '/admin/library/folder_permissions.mako', - folder=folder, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def library_dataset( self, trans, obj_id, library_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - if params.get( 'permissions', False ): - action = 'permissions' - else: - action = 'information' - library_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( obj_id ) - if not library_dataset: - msg = "Invalid library dataset specified, id: %s" %str( obj_id ) - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if action == 'information': - if params.get( 'edit_attributes_button', False ): - old_name = library_dataset.name - new_name = util.restore_text( params.get( 'name', '' ) ) - new_info = util.restore_text( params.get( 'info', '' ) ) - if not new_name: - msg = 'Enter a valid name' - messagetype = 'error' - else: - library_dataset.name = new_name - library_dataset.info = new_info - trans.sa_session.add( library_dataset ) - trans.sa_session.flush() - msg = "Dataset '%s' has been renamed to '%s'" % ( old_name, new_name ) - messagetype = 'done' - return trans.fill_template( '/admin/library/library_dataset_info.mako', - library_dataset=library_dataset, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - elif action == 'permissions': - if params.get( 'update_roles_button', False ): - # The user clicked the Save button on the 'Edit permissions and role associations' form - permissions = {} - for k, v in trans.app.model.Library.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - # Set the LIBRARY permissions on the LibraryDataset - # NOTE: the LibraryDataset and LibraryDatasetDatasetAssociation will be set with the same permissions - trans.app.security_agent.set_all_library_permissions( library_dataset, permissions ) - trans.sa_session.refresh( library_dataset ) - # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation - trans.app.security_agent.set_all_library_permissions( library_dataset.library_dataset_dataset_association, permissions ) - trans.sa_session.refresh( library_dataset.library_dataset_dataset_association ) - msg = 'Permissions and roles have been updated for library dataset %s' % library_dataset.name - return trans.fill_template( '/admin/library/library_dataset_permissions.mako', - library_dataset=library_dataset, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def ldda_edit_info( self, trans, library_id, folder_id, obj_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( obj_id ) - if not ldda: - msg = "Invalid LibraryDatasetDatasetAssociation specified, obj_id: %s" % str( obj_id ) - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - dbkey = params.get( 'dbkey', '?' ) - if isinstance( dbkey, list ): - dbkey = dbkey[0] - file_formats = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ] - file_formats.sort() - # See if we have any associated templates - widgets = ldda.get_template_widgets( trans ) - if params.get( 'change', False ): - # The user clicked the Save button on the 'Change data type' form - if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( params.datatype ).allow_datatype_change: - trans.app.datatypes_registry.change_datatype( ldda, params.datatype ) - trans.sa_session.flush() - msg = "Data type changed for library dataset '%s'" % ldda.name - return trans.fill_template( "/admin/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - else: - return trans.show_error_message( "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % \ - ( ldda.extension, params.datatype ) ) - elif params.get( 'save', False ): - # The user clicked the Save button on the 'Edit Attributes' form - old_name = ldda.name - new_name = util.restore_text( params.get( 'name', '' ) ) - new_info = util.restore_text( params.get( 'info', '' ) ) - new_message = util.restore_text( params.get( 'message', '' ) ) - if not new_name: - msg = 'Enter a valid name' - messagetype = 'error' - else: - ldda.name = new_name - ldda.info = new_info - ldda.message = new_message - # The following for loop will save all metadata_spec items - for name, spec in ldda.datatype.metadata_spec.items(): - if spec.get("readonly"): - continue - optional = params.get( "is_" + name, None ) - if optional and optional == 'true': - # optional element... == 'true' actually means it is NOT checked (and therefore ommitted) - setattr( ldda.metadata, name, None ) - else: - setattr( ldda.metadata, name, spec.unwrap( params.get ( name, None ) ) ) - ldda.metadata.dbkey = dbkey - ldda.datatype.after_setting_metadata( ldda ) - trans.sa_session.flush() - msg = 'Attributes updated for library dataset %s' % ldda.name - messagetype = 'done' - return trans.fill_template( "/admin/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - elif params.get( 'detect', False ): - # The user clicked the Auto-detect button on the 'Edit Attributes' form - for name, spec in ldda.datatype.metadata_spec.items(): - # We need to be careful about the attributes we are resetting - if name not in [ 'name', 'info', 'dbkey' ]: - if spec.get( 'default' ): - setattr( ldda.metadata, name, spec.unwrap( spec.get( 'default' ) ) ) - ldda.datatype.set_meta( ldda ) - ldda.datatype.after_setting_metadata( ldda ) - trans.sa_session.flush() - msg = 'Attributes updated for library dataset %s' % ldda.name - return trans.fill_template( "/admin/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - elif params.get( 'delete', False ): - ldda.deleted = True - trans.sa_session.add( ldda ) - trans.sa_session.flush() - msg = 'Dataset %s has been removed from this data library' % ldda.name - return trans.fill_template( "/admin/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - if "dbkey" in ldda.datatype.metadata_spec and not ldda.metadata.dbkey: - # Copy dbkey into metadata, for backwards compatability - # This looks like it does nothing, but getting the dbkey - # returns the metadata dbkey unless it is None, in which - # case it resorts to the old dbkey. Setting the dbkey - # sets it properly in the metadata - ldda.metadata.dbkey = ldda.dbkey - return trans.fill_template( "/admin/library/ldda_edit_info.mako", - ldda=ldda, - library_id=library_id, - file_formats=file_formats, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def ldda_display_info( self, trans, library_id, folder_id, obj_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( obj_id ) - if not ldda: - msg = "Invalid LibraryDatasetDatasetAssociation specified, obj_id: %s" % str( obj_id ) - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - # See if we have any associated templates - widgets = ldda.get_template_widgets( trans ) - return trans.fill_template( '/admin/library/ldda_info.mako', - ldda=ldda, - library_id=library_id, - show_deleted=show_deleted, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def ldda_manage_permissions( self, trans, library_id, folder_id, obj_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - obj_ids = util.listify( obj_id ) - # Display permission form, permissions will be updated for all lddas simultaneously. - lddas = [] - for obj_id in [ int( obj_id ) for obj_id in obj_ids ]: - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( obj_id ) - if ldda is None: - msg = 'You specified an invalid LibraryDatasetDatasetAssociation obj_id: %s' %str( obj_id ) - trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - lddas.append( ldda ) - if params.get( 'update_roles_button', False ): - permissions = {} - accessible = False - for k, v in trans.app.model.Dataset.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ] - # At least 1 user must have every role associated with this dataset, or the dataset is inaccessible - if v == trans.app.security_agent.permitted_actions.DATASET_ACCESS: - if len( in_roles ) > 1: - # Get the set of all users that are being associated with the dataset - in_roles_set = set() - for role in in_roles: - in_roles_set.add( role ) - users_set = set() - for role in in_roles: - for ura in role.users: - users_set.add( ura.user ) - # Make sure that at least 1 user has every role being associated with the dataset - for user in users_set: - user_roles_set = set() - for ura in user.roles: - user_roles_set.add( ura.role ) - if in_roles_set.issubset( user_roles_set ): - accessible = True - break - else: - accessible = True - if not accessible and v == trans.app.security_agent.permitted_actions.DATASET_ACCESS: - # Don't set the permissions for DATASET_ACCESS if inaccessbile, but set all other permissions - # TODO: keep access permissions as they originally were, rather than automatically making public - permissions[ trans.app.security_agent.get_action( v.action ) ] = [] - else: - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - for ldda in lddas: - # Set the DATASET permissions on the Dataset - trans.app.security_agent.set_all_dataset_permissions( ldda.dataset, permissions ) - trans.sa_session.refresh( ldda.dataset ) - permissions = {} - for k, v in trans.app.model.Library.permitted_actions.items(): - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - for ldda in lddas: - # Set the LIBRARY permissions on the LibraryDataset - # NOTE: the LibraryDataset and LibraryDatasetDatasetAssociation will be set with the same permissions - trans.app.security_agent.set_all_library_permissions( ldda.library_dataset, permissions ) - trans.sa_session.refresh( ldda.library_dataset ) - # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation - trans.app.security_agent.set_all_library_permissions( ldda, permissions ) - trans.sa_session.refresh( ldda ) - if not accessible: - msg = "At least 1 user must have every role associated with accessing these %d datasets. " % len( lddas ) - msg += "The roles you attempted to associate for access would make these datasets inaccessible by everyone, " - msg += "so access permissions were not set. All other permissions were updated for the datasets." - messagetype = 'error' - else: - msg = "Permissions have been updated on %d datasets" % len( lddas ) - return trans.fill_template( "/admin/library/ldda_permissions.mako", - lddas=lddas, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - if len( obj_ids ) > 1: - # Ensure that the permissions across all library items are identical, otherwise we can't update them together. - check_list = [] - for ldda in lddas: - permissions = [] - # Check the library level permissions - the permissions on the LibraryDatasetDatasetAssociation - # will always be the same as the permissions on the associated LibraryDataset, so we only need to - # check one Library object - for library_permission in trans.app.security_agent.get_library_dataset_permissions( ldda.library_dataset ): - if library_permission.action not in permissions: - permissions.append( library_permission.action ) - for dataset_permission in trans.app.security_agent.get_dataset_permissions( ldda.dataset ): - if dataset_permission.action not in permissions: - permissions.append( dataset_permission.action ) - permissions.sort() - if not check_list: - check_list = permissions - if permissions != check_list: - msg = 'The datasets you selected do not have identical permissions, so they can not be updated together' - trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - return trans.fill_template( "/admin/library/ldda_permissions.mako", - lddas=lddas, - library_id=library_id, - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def upload_library_dataset( self, trans, library_id, folder_id, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - deleted = util.string_as_bool( params.get( 'deleted', False ) ) - show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) - dbkey = params.get( 'dbkey', '?' ) - if isinstance( dbkey, list ): - last_used_build = dbkey[0] - else: - last_used_build = dbkey - folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder_id ) - if folder and last_used_build in [ 'None', None, '?' ]: - last_used_build = folder.genome_build - replace_id = params.get( 'replace_id', None ) - if replace_id not in [ None, 'None' ]: - replace_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( int( replace_id ) ) - # The name is separately - by the time the new ldda is created, - # replace_dataset.name will point to the new ldda, not the one it's - # replacing. - replace_dataset_name = replace_dataset.name - if not last_used_build: - last_used_build = replace_dataset.library_dataset_dataset_association.dbkey - # Don't allow multiple datasets to be uploaded when replacing a dataset with a new version - upload_option = 'upload_file' - else: - replace_dataset = None - upload_option = params.get( 'upload_option', 'upload_file' ) - if params.get( 'runtool_btn', False ) or params.get( 'ajax_upload', False ): - # See if we have any inherited templates, but do not inherit contents. - info_association, inherited = folder.get_info_association( inherited=True ) - if info_association: - template_id = str( info_association.template.id ) - widgets = folder.get_template_widgets( trans, get_contents=False ) - else: - template_id = 'None' - widgets = [] - created_outputs = trans.webapp.controllers[ 'library_common' ].upload_dataset( trans, - controller='library_admin', - library_id=library_id, - folder_id=folder_id, - template_id=template_id, - widgets=widgets, - replace_dataset=replace_dataset, - **kwd ) - if created_outputs: - total_added = len( created_outputs.values() ) - if replace_dataset: - msg = "Added %d dataset versions to the library dataset '%s' in the folder '%s'." % ( total_added, replace_dataset_name, folder.name ) - else: - if not folder.parent: - # Libraries have the same name as their root_folder - msg = "Added %d datasets to the library '%s' ( each is selected ). " % ( total_added, folder.name ) - else: - msg = "Added %d datasets to the folder '%s' ( each is selected ). " % ( total_added, folder.name ) - msg += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." - messagetype='done' - else: - msg = "Upload failed" - messagetype='error' - trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - created_ldda_ids=",".join( [ str( v.id ) for v in created_outputs.values() ] ), - msg=util.sanitize_text( msg ), - messagetype=messagetype ) ) - # See if we have any inherited templates, but do not inherit contents. - widgets = folder.get_template_widgets( trans, get_contents=False ) - upload_option = params.get( 'upload_option', 'upload_file' ) - # No dataset(s) specified, so display the upload form. Send list of data formats to the form - # so the "extension" select list can be populated dynamically - file_formats = trans.app.datatypes_registry.upload_file_formats - # Send list of genome builds to the form so the "dbkey" select list can be populated dynamically - def get_dbkey_options( last_used_build ): - for dbkey, build_name in util.dbnames: - yield build_name, dbkey, ( dbkey==last_used_build ) - dbkeys = get_dbkey_options( last_used_build ) - # Send list of roles to the form so the dataset can be associated with 1 or more of them. - roles = trans.sa_session.query( trans.app.model.Role ) \ - .filter( trans.app.model.Role.table.c.deleted==False ) \ - .order_by( trans.app.model.Role.table.c.name ) - # Send the current history to the form to enable importing datasets from history to library - history = trans.get_history() - trans.sa_session.refresh( history ) - # If we're using nginx upload, override the form action - action = web.url_for( controller='library_admin', action='upload_library_dataset' ) - if upload_option == 'upload_file' and trans.app.config.nginx_upload_path: - action = web.url_for( trans.app.config.nginx_upload_path ) + '?nginx_redir=' + action - return trans.fill_template( '/admin/library/upload.mako', - upload_option=upload_option, - action=action, - library_id=library_id, - folder_id=folder_id, - replace_dataset=replace_dataset, - file_formats=file_formats, - dbkeys=dbkeys, - last_used_build=last_used_build, - roles=roles, - history=history, - widgets=widgets, - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def add_history_datasets_to_library( self, trans, library_id, folder_id, hda_ids='', **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - try: - folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( int( folder_id ) ) - except: - msg = "Invalid folder id: %s" % str( folder_id ) - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - replace_id = params.get( 'replace_id', None ) - if replace_id: - replace_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( replace_id ) - else: - replace_dataset = None - # See if the current history is empty - history = trans.get_history() - trans.sa_session.refresh( history ) - if not history.active_datasets: - msg = 'Your current history is empty' - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if params.get( 'add_history_datasets_to_library_button', False ): - hda_ids = util.listify( hda_ids ) - if hda_ids: - dataset_names = [] - created_ldda_ids = '' - for hda_id in hda_ids: - hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( hda_id ) - if hda: - ldda = hda.to_library_dataset_dataset_association( target_folder=folder, replace_dataset=replace_dataset ) - created_ldda_ids = '%s,%s' % ( created_ldda_ids, str( ldda.id ) ) - dataset_names.append( ldda.name ) - if not replace_dataset: - # If replace_dataset is None, the Library level permissions will be taken from the folder and applied to the new - # LDDA and LibraryDataset. - trans.app.security_agent.copy_library_permissions( folder, ldda ) - trans.app.security_agent.copy_library_permissions( folder, ldda.library_dataset ) - # Permissions must be the same on the LibraryDatasetDatasetAssociation and the associated LibraryDataset - trans.app.security_agent.copy_library_permissions( ldda.library_dataset, ldda ) - else: - msg = "The requested HistoryDatasetAssociation id %s is invalid" % str( hda_id ) - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if created_ldda_ids: - created_ldda_ids = created_ldda_ids.lstrip( ',' ) - ldda_id_list = created_ldda_ids.split( ',' ) - total_added = len( ldda_id_list ) - if replace_dataset: - msg = "Added %d dataset versions to the library dataset '%s' in the folder '%s'." % ( total_added, replace_dataset.name, folder.name ) - else: - if not folder.parent: - # Libraries have the same name as their root_folder - msg = "Added %d datasets to the library '%s' ( each is selected ). " % ( total_added, folder.name ) - else: - msg = "Added %d datasets to the folder '%s' ( each is selected ). " % ( total_added, folder.name ) - msg += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." - return trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - created_ldda_ids=created_ldda_ids, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - else: - msg = 'Select at least one dataset from the list of active datasets in your current history' - messagetype = 'error' - last_used_build = folder.genome_build - upload_option = params.get( 'upload_option', 'import_from_history' ) - # Send list of data formats to the form so the "extension" select list can be populated dynamically - file_formats = trans.app.datatypes_registry.upload_file_formats - # Send list of genome builds to the form so the "dbkey" select list can be populated dynamically - def get_dbkey_options( last_used_build ): - for dbkey, build_name in util.dbnames: - yield build_name, dbkey, ( dbkey==last_used_build ) - dbkeys = get_dbkey_options( last_used_build ) - # Send list of roles to the form so the dataset can be associated with 1 or more of them. - roles = trans.sa_session.query( trans.app.model.Role ) \ - .filter( trans.app.model.Role.table.c.deleted==False ) \ - .order_by( trans.app.model.Role.table.c.name ) - return trans.fill_template( "/admin/library/upload.mako", - upload_option=upload_option, - library_id=library_id, - folder_id=folder_id, - replace_dataset=replace_dataset, - file_formats=file_formats, - dbkeys=dbkeys, - last_used_build=last_used_build, - roles=roles, - history=history, - widgets=[], - msg=msg, - messagetype=messagetype ) - @web.expose - @web.require_admin - def datasets( self, trans, library_id, **kwd ): - # This method is used by the select list labeled "Perform action on selected datasets" - # on the admin library browser. - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - if params.get( 'action_on_datasets_button', False ): - ldda_ids = util.listify( params.get( 'ldda_ids', None ) ) - if not ldda_ids: - msg = "At least one dataset must be selected for %s" % params.action - trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype='error' ) ) - if params.action == 'manage_permissions': - # We need the folder containing the LibraryDatasetDatasetAssociation(s) - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( int( ldda_ids[0] ) ) - trans.response.send_redirect( web.url_for( controller='library_admin', - action='ldda_manage_permissions', - library_id=library_id, - folder_id=ldda.library_dataset.folder.id, - obj_id=",".join( ldda_ids ), - msg=util.sanitize_text( msg ), - messagetype=messagetype ) ) - elif params.action == 'delete': - for ldda_id in ldda_ids: - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( ldda_id ) - ldda.deleted = True - trans.sa_session.add( ldda ) - trans.sa_session.flush() - msg = "The selected datasets have been removed from this data library" - trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - show_deleted=False, - msg=util.sanitize_text( msg ), - messagetype='done' ) ) - else: - trans.response.send_redirect( web.url_for( controller='library_admin', - action='browse_library', - obj_id=library_id, - msg=util.sanitize_text( msg ), - messagetype=messagetype ) ) + action='browse_libraries', + message=util.sanitize_text( msg ), + status='done' ) ) @web.expose @web.require_admin def delete_library_item( self, trans, library_id, library_item_id, library_item_type ): @@ -936,19 +190,21 @@ library_item_desc = 'Dataset' else: library_item_desc = library_item_type.capitalize() - library_item = trans.sa_session.query( library_item_types[ library_item_type ] ).get( int( library_item_id ) ) + library_item = trans.sa_session.query( library_item_types[ library_item_type ] ).get( trans.security.decode_id( library_item_id ) ) library_item.deleted = True trans.sa_session.add( library_item ) trans.sa_session.flush() msg = util.sanitize_text( "%s '%s' has been marked deleted" % ( library_item_desc, library_item.name ) ) messagetype = 'done' if library_item_type == 'library': - return self.browse_libraries( trans, msg=msg, messagetype=messagetype ) + return self.browse_libraries( trans, message=msg, status=messagetype ) else: - return self.browse_library( trans, - obj_id=library_id, - msg=msg, - messagetype=messagetype ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller='library_admin', + id=library_id, + msg=msg, + messagetype=messagetype ) ) @web.expose @web.require_admin def undelete_library_item( self, trans, library_id, library_item_id, library_item_type ): @@ -958,26 +214,28 @@ 'library_dataset': trans.app.model.LibraryDataset } if library_item_type not in library_item_types: msg = 'Bad library_item_type specified: %s' % str( library_item_type ) - messagetype = 'error' + status = ERROR else: if library_item_type == 'library_dataset': library_item_desc = 'Dataset' else: library_item_desc = library_item_type.capitalize() - library_item = trans.sa_session.query( library_item_types[ library_item_type ] ).get( int( library_item_id ) ) + library_item = trans.sa_session.query( library_item_types[ library_item_type ] ).get( trans.security.decode_id( library_item_id ) ) if library_item.purged: msg = '%s %s has been purged, so it cannot be undeleted' % ( library_item_desc, library_item.name ) - messagetype = 'error' + status = ERROR else: library_item.deleted = False trans.sa_session.add( library_item ) trans.sa_session.flush() msg = util.sanitize_text( "%s '%s' has been marked undeleted" % ( library_item_desc, library_item.name ) ) - messagetype = 'done' + status = SUCCESS if library_item_type == 'library': - return self.browse_libraries( trans, msg=msg, messagetype=messagetype ) + return self.browse_libraries( trans, message=msg, status=status ) else: - return self.browse_library( trans, - obj_id=library_id, - msg=msg, - messagetype=messagetype ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller='library_admin', + id=library_id, + msg=msg, + messagetype=status ) ) diff -r 9f966847abda -r 4743015d9a57 lib/galaxy/web/controllers/library_common.py --- a/lib/galaxy/web/controllers/library_common.py Tue Dec 22 18:49:34 2009 -0500 +++ b/lib/galaxy/web/controllers/library_common.py Wed Dec 23 20:26:01 2009 -0500 @@ -7,9 +7,50 @@ from galaxy.tools.actions import upload_common from galaxy.web.controllers.forms import get_all_forms from galaxy.model.orm import * +from galaxy.util.streamball import StreamBall +import logging, tempfile, zipfile, tarfile, os, sys + +if sys.version_info[:2] < ( 2, 6 ): + zipfile.BadZipFile = zipfile.error +if sys.version_info[:2] < ( 2, 5 ): + zipfile.LargeZipFile = zipfile.error log = logging.getLogger( __name__ ) +# Test for available compression types +tmpd = tempfile.mkdtemp() +comptypes = [] +for comptype in ( 'gz', 'bz2' ): + tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype ) + try: + archive = tarfile.open( tmpf, 'w:' + comptype ) + archive.close() + comptypes.append( comptype ) + except tarfile.CompressionError: + log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype ) + try: + os.unlink( tmpf ) + except OSError: + pass +ziptype = '32' +tmpf = os.path.join( tmpd, 'compression_test.zip' ) +try: + archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True ) + archive.close() + comptypes.append( 'zip' ) + ziptype = '64' +except RuntimeError: + log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." ) +except (TypeError, zipfile.LargeZipFile): + # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped + log.warning( 'Max zip file size is 2GB, ZIP64 not supported' ) + comptypes.append( 'zip' ) +try: + os.unlink( tmpf ) +except OSError: + pass +os.rmdir( tmpd ) + class LibraryCommon( BaseController ): @web.json def library_item_updates( self, trans, ids=None, states=None ): @@ -30,11 +71,661 @@ force_history_refresh = False rval[id] = { "state": data.state, - "html": unicode( trans.fill_template( "library/library_item_info.mako", ldda=data ), 'utf-8' ) + "html": unicode( trans.fill_template( "library/common/library_item_info.mako", ldda=data ), 'utf-8' ) #"force_history_refresh": force_history_refresh } return rval - def upload_dataset( self, trans, controller, library_id, folder_id, replace_dataset=None, **kwd ): + @web.expose + def browse_library( self, trans, cntrller, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + library_id = params.get( 'id', None ) + if not library_id: + # To handle bots + msg = "You must specify a library id." + return trans.response.send_redirect( web.url_for( controller=cntrller, + action='browse_libraries', + default_action=params.get( 'default_action', None ), + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) + if not library: + # To handle bots + msg = "Invalid library id ( %s )." % str( library_id ) + return trans.response.send_redirect( web.url_for( controller=cntrller, + action='browse_libraries', + default_action=params.get( 'default_action', None ), + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) + created_ldda_ids = params.get( 'created_ldda_ids', '' ) + hidden_folder_ids = util.listify( params.get( 'hidden_folder_ids', '' ) ) + if created_ldda_ids and not msg: + msg = "%d datasets are now uploading in the background to the library '%s' ( each is selected ). " % \ + ( len( created_ldda_ids.split( ',' ) ), library.name ) + msg += "Do not navigate away from Galaxy or use the browser's \"stop\" or \"reload\" buttons ( on this tab ) until the upload(s) change from the \"uploading\" state." + messagetype = "info" + return trans.fill_template( '/library/common/browse_library.mako', + cntrller=cntrller, + library=library, + created_ldda_ids=created_ldda_ids, + hidden_folder_ids=hidden_folder_ids, + default_action=params.get( 'default_action', None ), + show_deleted=show_deleted, + comptypes=comptypes, + msg=msg, + messagetype=messagetype ) + @web.expose + def library_info( self, trans, cntrller, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + library_id = params.get( 'id', None ) + library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) + # See if we have any associated templates + widgets = library.get_template_widgets( trans ) + if params.get( 'rename_library_button', False ): + old_name = library.name + new_name = util.restore_text( params.name ) + new_description = util.restore_text( params.description ) + if not new_name: + msg = 'Enter a valid name' + return trans.fill_template( '/library/common/library_info.mako', + cntrller=cntrller, + library=library, + widgets=widgets, + msg=msg, + messagetype='error' ) + else: + library.name = new_name + library.description = new_description + # Rename the root_folder + library.root_folder.name = new_name + library.root_folder.description = new_description + trans.sa_session.add_all( ( library, library.root_folder ) ) + trans.sa_session.flush() + msg = "Library '%s' has been renamed to '%s'" % ( old_name, new_name ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='library_info', + cntrller=cntrller, + id=trans.security.encode_id( library.id ), + msg=util.sanitize_text( msg ), + messagetype='done' ) ) + return trans.fill_template( '/library/common/library_info.mako', + cntrller=cntrller, + library=library, + widgets=widgets, + msg=msg, + messagetype=messagetype ) + @web.expose + def library_permissions( self, trans, cntrller, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + library_id = params.get( 'id', None ) + library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) + if params.get( 'update_roles_button', False ): + # The user clicked the Save button on the 'Associate With Roles' form + permissions = {} + for k, v in trans.app.model.Library.permitted_actions.items(): + in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ] + permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles + trans.app.security_agent.set_all_library_permissions( library, permissions ) + trans.sa_session.refresh( library ) + # Copy the permissions to the root folder + trans.app.security_agent.copy_library_permissions( library, library.root_folder ) + msg = "Permissions updated for library '%s'" % library.name + return trans.response.send_redirect( web.url_for( controller='library_common', + action='library_permissions', + cntrller=cntrller, + id=trans.security.encode_id( library.id ), + msg=util.sanitize_text( msg ), + messagetype='done' ) ) + return trans.fill_template( '/library/common/library_permissions.mako', + cntrller=cntrller, + library=library, + msg=msg, + messagetype=messagetype ) + @web.expose + def create_folder( self, trans, cntrller, parent_id, library_id, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( parent_id ) ) + if not folder: + msg = "Invalid parent folder id (%s) specified" % str( parent_id ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + if params.new == 'submitted': + new_folder = trans.app.model.LibraryFolder( name=util.restore_text( params.name ), + description=util.restore_text( params.description ) ) + # We are associating the last used genome build with folders, so we will always + # initialize a new folder with the first dbkey in util.dbnames which is currently + # ? unspecified (?) + new_folder.genome_build = util.dbnames.default_value + folder.add_folder( new_folder ) + trans.sa_session.add( new_folder ) + trans.sa_session.flush() + # New folders default to having the same permissions as their parent folder + trans.app.security_agent.copy_library_permissions( folder, new_folder ) + msg = "New folder named '%s' has been added to the library" % new_folder.name + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='done' ) ) + return trans.fill_template( '/library/common/new_folder.mako', + cntrller=cntrller, + library_id=library_id, + folder=folder, + msg=msg, + messagetype=messagetype ) + @web.expose + def folder_info( self, trans, cntrller, id, library_id, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( id ) ) + if cntrller != 'library_admin': + user, roles = trans.get_user_and_roles() + # See if we have any associated templates + widgets = folder.get_template_widgets( trans ) + if params.get( 'rename_folder_button', False ): + if cntrller=='library_admin' or trans.app.security_agent.can_modify_library_item( user, roles, folder ): + old_name = folder.name + new_name = util.restore_text( params.name ) + new_description = util.restore_text( params.description ) + if not new_name: + msg = 'Enter a valid name' + return trans.fill_template( "/library/common/folder_info.mako", + cntrller=cntrller, + folder=folder, + library_id=library_id, + widgets=widgets, + msg=msg, + messagetype='error' ) + else: + folder.name = new_name + folder.description = new_description + trans.sa_session.add( folder ) + trans.sa_session.flush() + msg = "Folder '%s' has been renamed to '%s'" % ( old_name, new_name ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='folder_info', + cntrller=cntrller, + id=id, + library_id=library_id, + msg=util.sanitize_text( msg ), + messagetype='done' ) ) + else: + msg = "You are not authorized to edit this folder" + return trans.fill_template( "/library/common/folder_info.mako", + cntrller=cntrller, + folder=folder, + library_id=library_id, + widgets=widgets, + msg=msg, + messagetype='error' ) + return trans.fill_template( '/library/common/folder_info.mako', + cntrller=cntrller, + folder=folder, + library_id=library_id, + widgets=widgets, + msg=msg, + messagetype=messagetype ) + @web.expose + def folder_permissions( self, trans, cntrller, id, library_id, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( int( id ) ) + if not folder: + msg = "Invalid folder specified, id: %s" % str( id ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + if cntrller == 'library': + user, roles = trans.get_user_and_roles() + if params.get( 'update_roles_button', False ): + # The user clicked the Save button on the 'Associate With Roles' form + if cntrller == 'library_admin' or trans.app.security_agent.can_manage_library_item( user, roles, folder ): + permissions = {} + for k, v in trans.app.model.Library.permitted_actions.items(): + in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( params.get( k + '_in', [] ) ) ] + permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles + trans.app.security_agent.set_all_library_permissions( folder, permissions ) + trans.sa_session.refresh( folder ) + msg = 'Permissions updated for folder %s' % folder.name + return trans.response.send_redirect( web.url_for( controller='library_common', + action='folder_permissions', + id=trans.security.encode_id( folder.id ), + library_id=library_id, + msg=util.sanitize_text( msg ), + messagetype='done' ) ) + else: + msg = "You are not authorized to manage permissions on this folder" + return trans.response.send_redirect( web.url_for( controller='library_common', + action='folder_permissions', + id=trans.security.encode_id( folder.id ), + library_id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + return trans.fill_template( '/library/common/folder_permissions.mako', + cntrller=cntrller, + folder=folder, + library_id=library_id, + msg=msg, + messagetype=messagetype ) + @web.expose + def ldda_edit_info( self, trans, cntrller, library_id, folder_id, id, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) ) + if not ldda: + msg = "Invalid LibraryDatasetDatasetAssociation specified, id: %s" % str( id ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + dbkey = params.get( 'dbkey', '?' ) + if isinstance( dbkey, list ): + dbkey = dbkey[0] + if cntrller == 'library': + user, roles = trans.get_user_and_roles() + file_formats = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ] + file_formats.sort() + # See if we have any associated templates + widgets = ldda.get_template_widgets( trans ) + if params.get( 'change', False ): + # The user clicked the Save button on the 'Change data type' form + if cntrller=='library_admin' or trans.app.security_agent.can_modify_library_item( user, roles, ldda ): + if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( params.datatype ).allow_datatype_change: + trans.app.datatypes_registry.change_datatype( ldda, params.datatype ) + trans.sa_session.flush() + msg = "Data type changed for library dataset '%s'" % ldda.name + messagetype = 'done' + else: + msg = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( ldda.extension, params.datatype ) + messagetype = 'error' + else: + msg = "You are not authorized to change the data type of dataset '%s'" % ldda.name + messagetype = 'error' + return trans.fill_template( "/library/common/ldda_edit_info.mako", + cntrller=cntrller, + ldda=ldda, + library_id=library_id, + file_formats=file_formats, + widgets=widgets, + msg=msg, + messagetype=messagetype ) + elif params.get( 'save', False ): + # The user clicked the Save button on the 'Edit Attributes' form + if cntrller=='library_admin' or trans.app.security_agent.can_modify_library_item( user, roles, ldda ): + old_name = ldda.name + new_name = util.restore_text( params.get( 'name', '' ) ) + new_info = util.restore_text( params.get( 'info', '' ) ) + new_message = util.restore_text( params.get( 'message', '' ) ) + if not new_name: + msg = 'Enter a valid name' + messagetype = 'error' + else: + ldda.name = new_name + ldda.info = new_info + ldda.message = new_message + # The following for loop will save all metadata_spec items + for name, spec in ldda.datatype.metadata_spec.items(): + if spec.get("readonly"): + continue + optional = params.get( "is_" + name, None ) + if optional and optional == 'true': + # optional element... == 'true' actually means it is NOT checked (and therefore ommitted) + setattr( ldda.metadata, name, None ) + else: + setattr( ldda.metadata, name, spec.unwrap( params.get ( name, None ) ) ) + ldda.metadata.dbkey = dbkey + ldda.datatype.after_setting_metadata( ldda ) + trans.sa_session.flush() + msg = 'Attributes updated for library dataset %s' % ldda.name + messagetype = 'done' + else: + msg = "You are not authorized to edit the attributes of dataset '%s'" % ldda.name + messagetype = 'error' + return trans.fill_template( "/library/common/ldda_edit_info.mako", + cntrller=cntrller, + ldda=ldda, + library_id=library_id, + file_formats=file_formats, + widgets=widgets, + msg=msg, + messagetype=messagetype ) + elif params.get( 'detect', False ): + # The user clicked the Auto-detect button on the 'Edit Attributes' form + if cntrller=='library_admin' or trans.app.security_agent.can_modify_library_item( user, roles, ldda ): + for name, spec in ldda.datatype.metadata_spec.items(): + # We need to be careful about the attributes we are resetting + if name not in [ 'name', 'info', 'dbkey' ]: + if spec.get( 'default' ): + setattr( ldda.metadata, name, spec.unwrap( spec.get( 'default' ) ) ) + ldda.datatype.set_meta( ldda ) + ldda.datatype.after_setting_metadata( ldda ) + trans.sa_session.flush() + msg = 'Attributes updated for library dataset %s' % ldda.name + messagetype = 'done' + else: + msg = "You are not authorized to edit the attributes of dataset '%s'" % ldda.name + messagetype = 'error' + return trans.fill_template( "/library/common/ldda_edit_info.mako", + cntrller=cntrller, + ldda=ldda, + library_id=library_id, + file_formats=file_formats, + widgets=widgets, + msg=msg, + messagetype=messagetype ) + if cntrller=='library_admin' or trans.app.security_agent.can_modify_library_item( user, roles, ldda ): + if "dbkey" in ldda.datatype.metadata_spec and not ldda.metadata.dbkey: + # Copy dbkey into metadata, for backwards compatability + # This looks like it does nothing, but getting the dbkey + # returns the metadata dbkey unless it is None, in which + # case it resorts to the old dbkey. Setting the dbkey + # sets it properly in the metadata + ldda.metadata.dbkey = ldda.dbkey + return trans.fill_template( "/library/common/ldda_edit_info.mako", + cntrller=cntrller, + ldda=ldda, + library_id=library_id, + file_formats=file_formats, + widgets=widgets, + msg=msg, + messagetype=messagetype ) + @web.expose + def ldda_display_info( self, trans, cntrller, library_id, folder_id, id, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) + ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) ) + if not ldda: + msg = "Invalid LibraryDatasetDatasetAssociation specified, id: %s" % str( id ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) + # See if we have any associated templates + widgets = ldda.get_template_widgets( trans ) + return trans.fill_template( '/library/common/ldda_info.mako', + cntrller=cntrller, + ldda=ldda, + library=library, + show_deleted=show_deleted, + widgets=widgets, + msg=msg, + messagetype=messagetype ) + @web.expose + def ldda_permissions( self, trans, cntrller, library_id, folder_id, id, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + ids = util.listify( id ) + # Display permission form, permissions will be updated for all lddas simultaneously. + lddas = [] + for id in [ trans.security.decode_id( id ) for id in ids ]: + ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( id ) + if ldda is None: + msg = 'You specified an invalid LibraryDatasetDatasetAssociation id: %s' %str( id ) + trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + lddas.append( ldda ) + if params.get( 'update_roles_button', False ): + if cntrller=='library_admin' or ( trans.app.security_agent.can_manage_library_item( user, roles, ldda ) and \ + trans.app.security_agent.can_manage_dataset( roles, ldda.dataset ) ): + permissions = {} + accessible = False + for k, v in trans.app.model.Dataset.permitted_actions.items(): + in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ] + # At least 1 user must have every role associated with this dataset, or the dataset is inaccessible + if v == trans.app.security_agent.permitted_actions.DATASET_ACCESS: + if len( in_roles ) > 1: + # Get the set of all users that are being associated with the dataset + in_roles_set = set() + for role in in_roles: + in_roles_set.add( role ) + users_set = set() + for role in in_roles: + for ura in role.users: + users_set.add( ura.user ) + # Make sure that at least 1 user has every role being associated with the dataset + for user in users_set: + user_roles_set = set() + for ura in user.roles: + user_roles_set.add( ura.role ) + if in_roles_set.issubset( user_roles_set ): + accessible = True + break + else: + accessible = True + if not accessible and v == trans.app.security_agent.permitted_actions.DATASET_ACCESS: + # Don't set the permissions for DATASET_ACCESS if inaccessbile, but set all other permissions + # TODO: keep access permissions as they originally were, rather than automatically making public + permissions[ trans.app.security_agent.get_action( v.action ) ] = [] + else: + permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles + for ldda in lddas: + # Set the DATASET permissions on the Dataset + trans.app.security_agent.set_all_dataset_permissions( ldda.dataset, permissions ) + trans.sa_session.refresh( ldda.dataset ) + permissions = {} + for k, v in trans.app.model.Library.permitted_actions.items(): + in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] + permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles + for ldda in lddas: + # Set the LIBRARY permissions on the LibraryDataset + # NOTE: the LibraryDataset and LibraryDatasetDatasetAssociation will be set with the same permissions + trans.app.security_agent.set_all_library_permissions( ldda.library_dataset, permissions ) + trans.sa_session.refresh( ldda.library_dataset ) + # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation + trans.app.security_agent.set_all_library_permissions( ldda, permissions ) + trans.sa_session.refresh( ldda ) + if not accessible: + msg = "At least 1 user must have every role associated with accessing these %d datasets. " % len( lddas ) + msg += "The roles you attempted to associate for access would make these datasets inaccessible by everyone, " + msg += "so access permissions were not set. All other permissions were updated for the datasets." + messagetype = 'error' + else: + msg = "Permissions have been updated on %d datasets" % len( lddas ) + else: + msg = "You are not authorized to change the permissions of dataset '%s'" % ldda.name + messagetype = 'error' + return trans.fill_template( "/library/common/ldda_permissions.mako", + cntrller=cntrller, + lddas=lddas, + library_id=library_id, + msg=msg, + messagetype=messagetype ) + if len( ids ) > 1: + # Ensure that the permissions across all library items are identical, otherwise we can't update them together. + check_list = [] + for ldda in lddas: + permissions = [] + # Check the library level permissions - the permissions on the LibraryDatasetDatasetAssociation + # will always be the same as the permissions on the associated LibraryDataset, so we only need to + # check one Library object + for library_permission in trans.app.security_agent.get_library_dataset_permissions( ldda.library_dataset ): + if library_permission.action not in permissions: + permissions.append( library_permission.action ) + for dataset_permission in trans.app.security_agent.get_dataset_permissions( ldda.dataset ): + if dataset_permission.action not in permissions: + permissions.append( dataset_permission.action ) + permissions.sort() + if not check_list: + check_list = permissions + if permissions != check_list: + msg = 'The datasets you selected do not have identical permissions, so they can not be updated together' + trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + return trans.fill_template( "/library/common/ldda_permissions.mako", + cntrller=cntrller, + lddas=lddas, + library_id=library_id, + msg=msg, + messagetype=messagetype ) + @web.expose + def upload_library_dataset( self, trans, cntrller, library_id, folder_id, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + deleted = util.string_as_bool( params.get( 'deleted', False ) ) + show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) ) + dbkey = params.get( 'dbkey', '?' ) + if isinstance( dbkey, list ): + last_used_build = dbkey[0] + else: + last_used_build = dbkey + folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) ) + if folder and last_used_build in [ 'None', None, '?' ]: + last_used_build = folder.genome_build + replace_id = params.get( 'replace_id', None ) + if replace_id not in [ None, 'None' ]: + replace_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( trans.security.decode_id( replace_id ) ) + # The name is separately - by the time the new ldda is created, + # replace_dataset.name will point to the new ldda, not the one it's + # replacing. + replace_dataset_name = replace_dataset.name + if not last_used_build: + last_used_build = replace_dataset.library_dataset_dataset_association.dbkey + # Don't allow multiple datasets to be uploaded when replacing a dataset with a new version + upload_option = 'upload_file' + else: + replace_dataset = None + upload_option = params.get( 'upload_option', 'upload_file' ) + if cntrller == 'library': + user, roles = trans.get_user_and_roles() + if cntrller == 'library_admin' or \ + ( trans.app.security_agent.can_add_library_item( user, roles, folder ) or \ + ( replace_dataset and trans.app.security_agent.can_modify_library_item( user, roles, replace_dataset ) ) ): + if params.get( 'runtool_btn', False ) or params.get( 'ajax_upload', False ): + # See if we have any inherited templates, but do not inherit contents. + info_association, inherited = folder.get_info_association( inherited=True ) + if info_association: + template_id = str( info_association.template.id ) + widgets = folder.get_template_widgets( trans, get_contents=False ) + else: + template_id = 'None' + widgets = [] + created_outputs = trans.webapp.controllers[ 'library_common' ].upload_dataset( trans, + cntrller=cntrller, + library_id=library_id, + folder_id=folder_id, + template_id=template_id, + widgets=widgets, + replace_dataset=replace_dataset, + **kwd ) + if created_outputs: + total_added = len( created_outputs.values() ) + if replace_dataset: + msg = "Added %d dataset versions to the library dataset '%s' in the folder '%s'." % ( total_added, replace_dataset_name, folder.name ) + else: + if not folder.parent: + # Libraries have the same name as their root_folder + msg = "Added %d datasets to the library '%s' ( each is selected ). " % ( total_added, folder.name ) + else: + msg = "Added %d datasets to the folder '%s' ( each is selected ). " % ( total_added, folder.name ) + if cntrller == 'library_admin': + msg += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." + messagetype='done' + else: + # Since permissions on all LibraryDatasetDatasetAssociations must be the same at this point, we only need + # to check one of them to see if the current user can manage permissions on them. + ldda_id_list = [ str( v.id ) for v in created_outputs.values() ] + check_ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( ldda_id_list[0] ) + if trans.app.security_agent.can_manage_library_item( user, roles, check_ldda ): + if replace_dataset: + default_action = '' + else: + msg += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." + default_action = 'manage_permissions' + else: + default_action = 'add' + trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + default_action=default_action, + created_ldda_ids=",".join( ldda_id_list ), + msg=util.sanitize_text( msg ), + messagetype='done' ) ) + + else: + msg = "Upload failed" + messagetype='error' + trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + created_ldda_ids=",".join( [ str( v.id ) for v in created_outputs.values() ] ), + msg=util.sanitize_text( msg ), + messagetype=messagetype ) ) + # See if we have any inherited templates, but do not inherit contents. + widgets = folder.get_template_widgets( trans, get_contents=False ) + upload_option = params.get( 'upload_option', 'upload_file' ) + # No dataset(s) specified, so display the upload form. Send list of data formats to the form + # so the "extension" select list can be populated dynamically + file_formats = trans.app.datatypes_registry.upload_file_formats + # Send list of genome builds to the form so the "dbkey" select list can be populated dynamically + def get_dbkey_options( last_used_build ): + for dbkey, build_name in util.dbnames: + yield build_name, dbkey, ( dbkey==last_used_build ) + dbkeys = get_dbkey_options( last_used_build ) + # Send list of roles to the form so the dataset can be associated with 1 or more of them. + roles = trans.sa_session.query( trans.app.model.Role ) \ + .filter( trans.app.model.Role.table.c.deleted==False ) \ + .order_by( trans.app.model.Role.table.c.name ) + # Send the current history to the form to enable importing datasets from history to library + history = trans.get_history() + trans.sa_session.refresh( history ) + # If we're using nginx upload, override the form action + action = web.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller ) + if upload_option == 'upload_file' and trans.app.config.nginx_upload_path: + action = web.url_for( trans.app.config.nginx_upload_path ) + '?nginx_redir=' + action + return trans.fill_template( '/library/common/upload.mako', + cntrller=cntrller, + upload_option=upload_option, + action=action, + library_id=library_id, + folder_id=folder_id, + replace_dataset=replace_dataset, + file_formats=file_formats, + dbkeys=dbkeys, + last_used_build=last_used_build, + roles=roles, + history=history, + widgets=widgets, + msg=msg, + messagetype=messagetype ) + def upload_dataset( self, trans, cntrller, library_id, folder_id, replace_dataset=None, **kwd ): # Set up the traditional tool state/params tool_id = 'upload1' tool = trans.app.toolbox.tools_by_id[ tool_id ] @@ -52,7 +743,7 @@ messagetype = params.get( 'messagetype', 'done' ) server_dir = util.restore_text( params.get( 'server_dir', '' ) ) if replace_dataset not in [ None, 'None' ]: - replace_id = replace_dataset.id + replace_id = trans.security.encode_id( replace_dataset.id ) else: replace_id = None upload_option = params.get( 'upload_option', 'upload_file' ) @@ -60,7 +751,7 @@ if upload_option == 'upload_directory': if server_dir in [ None, 'None', '' ]: err_redirect = True - if controller == 'library_admin': + if cntrller == 'library_admin': import_dir = trans.app.config.library_import_dir import_dir_desc = 'library_import_dir' full_dir = os.path.join( import_dir, server_dir ) @@ -76,7 +767,7 @@ else: msg = '"%s" is not defined in the Galaxy configuration file' % import_dir_desc # Proceed with (mostly) regular upload processing - precreated_datasets = upload_common.get_precreated_datasets( trans, tool_params, trans.app.model.LibraryDatasetDatasetAssociation, controller=controller ) + precreated_datasets = upload_common.get_precreated_datasets( trans, tool_params, trans.app.model.LibraryDatasetDatasetAssociation, controller=cntrller ) if upload_option == 'upload_file': tool_params = upload_common.persist_uploads( tool_params ) uploaded_datasets = upload_common.get_uploaded_datasets( trans, tool_params, precreated_datasets, dataset_upload_inputs, library_bunch=library_bunch ) @@ -89,8 +780,9 @@ msg = 'Select a file, enter a URL or enter text' err_redirect = True if err_redirect: - trans.response.send_redirect( web.url_for( controller=controller, + trans.response.send_redirect( web.url_for( controller='library_common', action='upload_library_dataset', + cntrller=cntrller, library_id=library_id, folder_id=folder_id, replace_id=replace_id, @@ -182,15 +874,136 @@ return None, err_redirect, msg return uploaded_datasets, None, None @web.expose - def download_dataset_from_folder( self, trans, cntrller, obj_id, library_id=None, **kwd ): + def add_history_datasets_to_library( self, trans, cntrller, library_id, folder_id, hda_ids='', **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + try: + folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) ) + except: + msg = "Invalid folder id: %s" % str( folder_id ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + replace_id = params.get( 'replace_id', None ) + if replace_id: + replace_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( trans.security.decode_id( replace_id ) ) + else: + replace_dataset = None + # See if the current history is empty + history = trans.get_history() + trans.sa_session.refresh( history ) + if not history.active_datasets: + msg = 'Your current history is empty' + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + if params.get( 'add_history_datasets_to_library_button', False ): + hda_ids = util.listify( hda_ids ) + if hda_ids: + dataset_names = [] + created_ldda_ids = '' + for hda_id in hda_ids: + hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( hda_id ) ) + if hda: + ldda = hda.to_library_dataset_dataset_association( target_folder=folder, replace_dataset=replace_dataset ) + created_ldda_ids = '%s,%s' % ( created_ldda_ids, str( ldda.id ) ) + dataset_names.append( ldda.name ) + if not replace_dataset: + # If replace_dataset is None, the Library level permissions will be taken from the folder and applied to the new + # LDDA and LibraryDataset. + trans.app.security_agent.copy_library_permissions( folder, ldda ) + trans.app.security_agent.copy_library_permissions( folder, ldda.library_dataset ) + # Permissions must be the same on the LibraryDatasetDatasetAssociation and the associated LibraryDataset + trans.app.security_agent.copy_library_permissions( ldda.library_dataset, ldda ) + else: + msg = "The requested HistoryDatasetAssociation id %s is invalid" % str( hda_id ) + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + msg=util.sanitize_text( msg ), + messagetype='error' ) ) + if created_ldda_ids: + created_ldda_ids = created_ldda_ids.lstrip( ',' ) + ldda_id_list = created_ldda_ids.split( ',' ) + total_added = len( ldda_id_list ) + if replace_dataset: + msg = "Added %d dataset versions to the library dataset '%s' in the folder '%s'." % ( total_added, replace_dataset.name, folder.name ) + else: + if not folder.parent: + # Libraries have the same name as their root_folder + msg = "Added %d datasets to the library '%s' ( each is selected ). " % ( total_added, folder.name ) + else: + msg = "Added %d datasets to the folder '%s' ( each is selected ). " % ( total_added, folder.name ) + if cntrller == 'library_admin': + msg += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." + else: + # Since permissions on all LibraryDatasetDatasetAssociations must be the same at this point, we only need + # to check one of them to see if the current user can manage permissions on them. + check_ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( ldda_id_list[0] ) ) + user, roles = trans.get_user_and_roles() + if trans.app.security_agent.can_manage_library_item( user, roles, check_ldda ): + if replace_dataset: + default_action = '' + else: + msg += "Click the Go button at the bottom of this page to edit the permissions on these datasets if necessary." + default_action = 'manage_permissions' + else: + default_action = 'add' + return trans.response.send_redirect( web.url_for( controller='library_common', + action='browse_library', + cntrller=cntrller, + id=library_id, + created_ldda_ids=created_ldda_ids, + msg=util.sanitize_text( msg ), + messagetype='done' ) ) + else: + msg = 'Select at least one dataset from the list of active datasets in your current history' + messagetype = 'error' + last_used_build = folder.genome_build + upload_option = params.get( 'upload_option', 'import_from_history' ) + # Send list of data formats to the form so the "extension" select list can be populated dynamically + file_formats = trans.app.datatypes_registry.upload_file_formats + # Send list of genome builds to the form so the "dbkey" select list can be populated dynamically + def get_dbkey_options( last_used_build ): + for dbkey, build_name in util.dbnames: + yield build_name, dbkey, ( dbkey==last_used_build ) + dbkeys = get_dbkey_options( last_used_build ) + # Send list of roles to the form so the dataset can be associated with 1 or more of them. + roles = trans.sa_session.query( trans.app.model.Role ) \ + .filter( trans.app.model.Role.table.c.deleted==False ) \ + .order_by( trans.app.model.Role.table.c.name ) + return trans.fill_template( "/library/common/upload.mako", + upload_option=upload_option, + library_id=library_id, + folder_id=folder_id, + replace_dataset=replace_dataset, + file_formats=file_formats, + dbkeys=dbkeys, + last_used_build=last_used_build, + roles=roles, + history=history, + widgets=[], + msg=msg, + messagetype=messagetype ) + @web.expose + def download_dataset_from_folder( self, trans, cntrller, id, library_id=None, **kwd ): """Catches the dataset id and displays file contents as directed""" # id must refer to a LibraryDatasetDatasetAssociation object - ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( obj_id ) + ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( id ) if not ldda.dataset: - msg = 'Invalid LibraryDatasetDatasetAssociation id %s received for file downlaod' % str( obj_id ) - return trans.response.send_redirect( web.url_for( controller=cntrller, + msg = 'Invalid LibraryDatasetDatasetAssociation id %s received for file downlaod' % str( id ) + return trans.response.send_redirect( web.url_for( controller='library_common', action='browse_library', - obj_id=library_id, + cntrller=cntrller, + id=library_id, msg=util.sanitize_text( msg ), messagetype='error' ) ) mime = trans.app.datatypes_registry.get_mimetype_by_extension( ldda.extension.lower() ) @@ -200,38 +1013,263 @@ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' fname = ldda.name fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ] - trans.response.headers[ "Content-Disposition" ] = "attachment; filename=GalaxyLibraryDataset-%s-[%s]" % ( str( obj_id ), fname ) + trans.response.headers[ "Content-Disposition" ] = "attachment; filename=GalaxyLibraryDataset-%s-[%s]" % ( str( id ), fname ) try: return open( ldda.file_name ) except: msg = 'This dataset contains no content' - return trans.response.send_redirect( web.url_for( controller=cntrller, + return trans.response.send_redirect( web.url_for( controller='library_common', action='browse_library', - obj_id=library_id, + cntrller=cntrller, + id=library_id, msg=util.sanitize_text( msg ), messagetype='error' ) ) @web.expose - def info_template( self, trans, cntrller, library_id, response_action='library', obj_id=None, folder_id=None, ldda_id=None, **kwd ):