1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/changeset/a995f7aca75c/ changeset: a995f7aca75c user: smcmanus date: 2012-09-27 21:42:11 summary: First cut of query optimizations for browsing. At least 3 out of 5 queries were eliminated. Code is not active by default. affected #: 4 files diff -r 4f788268c76dcb77e99076dbfbc8cf65c05b00cc -r a995f7aca75c03bd180689aead6a8870928d8fdf lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -1785,6 +1785,7 @@ # we'll return the next available info_association in the inheritable hierarchy. # True is also returned if the info_association was inherited, and False if not. # This enables us to eliminate displaying any contents of the inherited template. + # SM: Accessing self.info_association can cause a query to be emitted if self.info_association: return self.info_association[0], inherited if restrict: diff -r 4f788268c76dcb77e99076dbfbc8cf65c05b00cc -r a995f7aca75c03bd180689aead6a8870928d8fdf lib/galaxy/security/__init__.py --- a/lib/galaxy/security/__init__.py +++ b/lib/galaxy/security/__init__.py @@ -225,13 +225,16 @@ # If role.type is neither private nor sharing, it's ok to display return True return role.type != self.model.Role.types.PRIVATE and role.type != self.model.Role.types.SHARING + def allow_action( self, roles, action, item ): """ Method for checking a permission for the current user ( based on roles ) to perform a specific action on an item, which must be one of: Dataset, Library, LibraryFolder, LibraryDataset, LibraryDatasetDatasetAssociation """ + # SM: Note that calling get_item_actions will emit a query. item_actions = self.get_item_actions( action, item ) + if not item_actions: return action.model == 'restrict' ret_val = False @@ -249,8 +252,152 @@ ret_val = True break return ret_val - def can_access_dataset( self, roles, dataset ): - return self.dataset_is_public( dataset ) or self.allow_action( roles, self.permitted_actions.DATASET_ACCESS, dataset ) + + + def get_actions_for_items( self, trans, action, permission_items ): + # TODO: Rename this; it's a replacement for get_item_actions, but it + # doesn't represent what it's really confusing. + # TODO: Make this work for other classes besides lib_datasets. + # That should be as easy as checking the type and writing a query for each; + # we're avoiding using the SQLAlchemy backrefs because they can cause lots + # of queries to be generated. + # + # Originally, get_item_actions did: + # return [ permission for permission in item.actions if permission.action == action.action ] + # The "item" can be just about anything with permissions, and referencing + # item.actions causes the item's permissions to be retrieved. + # This method will retrieve all permissions for all "items" and only + # return the permissions associated with that given action. + # We initialize the permissions list to be empty; we will return an + # empty list by default. + # + # If the dataset id has no corresponding action in its permissions, + # then the returned permissions will not carry an entry for the dataset. + ret_permissions = {} + if ( len( permission_items ) > 0 ): + if ( isinstance( permission_items[0], trans.model.LibraryDataset ) ): + ids = [ item.library_dataset_id for item in permission_items ] + permissions = trans.sa_session.query( trans.model.LibraryDatasetPermissions ) \ + .filter( and_( trans.model.LibraryDatasetPermissions.library_dataset_id.in_( ids ), + trans.model.LibraryDatasetPermissions.action == action ) ) \ + .all() + + # Massage the return data. We will return a list of permissions + # for each library dataset. So we initialize the return list to + # have an empty list for each dataset. Then each permission is + # appended to the right lib dataset. + # TODO: Consider eliminating the initialization and just return + # empty values for each library dataset id. + for item in permission_items: + ret_permissions[ item.library_dataset_id ] = [] + for permission in permissions: + ret_permissions[ permission.library_dataset_id ].append( permission ) + + # Test that we get the same response from get_item_actions each item: + test_code = False + if test_code: + try: + log.debug( "get_actions_for_items: Test start" ) + for item in permission_items: + base_result = self.get_item_actions( action, item ) + new_result = ret_permissions[ item.library_dataset_id ] + # For now, just test against LibraryDatasetIds; other classes + # are not tested yet. + if len( base_result ) == len( new_result ): + common_result = set(base_result).intersection( new_result ) + if len( common_result ) == len( base_result ): + log.debug( "Match on permissions for id %d" % + item.library_dataset_id ) + # TODO: Fix this failure message: + else: + log.debug( "Error: dataset %d; originally: %s; now: %s" + % ( item.library_dataset_id, + base_result, new_result ) ) + else: + log.debug( "Error: dataset %d: had %d entries, now %d entries" + % ( item.library_dataset_id, len( base_result ), + len( new_result ) ) ) + log.debug( "get_actions_for_items: Test end" ) + except Exception as e: + log.debug( "Exception in test code: %s" % e ) + + return ret_permissions + + + def allow_action_on_items( self, trans, user_roles, action, items ): + """ + This should be the equivalent of allow_action defined on multiple items. + It is meant to specifically replace allow_action for multiple + LibraryDatasets, but it could be reproduced or modified for + allow_action's permitted classes - Dataset, Library, LibraryFolder, and + LDDAs. + """ + all_items_actions = self.get_actions_for_items( trans, action, items ) + + ret_allow_action = {} + # Change item to lib_dataset or vice-versa. + for item in items: + if all_items_actions.has_key( item.id ): + item_actions = all_items_actions[ item.id ] + + # For access, all of the dataset's + if self.permitted_actions.DATASET_ACCESS == action: + ret_allow_action[ item.id ] = True + for item_action in item_actions: + if item_action.role not in user_roles: + ret_allow_action[ item.id ] = False + break + + # Else look for just one dataset role to be in the list of + # acceptable user roles: + else: + ret_allow_action[ item.id ] = False + for item_action in item_actions: + if item_action.role in user_roles: + ret_allow_action[ item.id ] = True + break + + else: + if 'restrict' == action.model: + ret_allow_action[ item.id ] = True + else: + ret_allow_action[ item.id ] = False + + # Test it: the result for each dataset should match the result for + # allow_action: + test_code = False + if test_code: + log.debug( "allow_action_for_items: test start" ) + for item in items: + orig_value = self.allow_action( user_roles, action, item ) + if orig_value == ret_allow_action[ item.id ]: + log.debug( "Item %d: success" % item.id ) + else: + log.debug( "Item %d: fail: original: %s; new: %s" + % ( item.id, orig_value, ret_allow_action[ item.id ] ) ) + log.debug( "allow_action_for_items: test end" ) + return ret_allow_action + + + def get_dataset_access_mapping( self, trans, user_roles, datasets ): + ''' + For the given list of datasets, return a mapping of the datasets' ids + to whether they can be accessed by the user or not. The datasets input + is expected to be a simple list of Dataset objects. + ''' + datasets_public_map = self.datasets_are_public( trans, datasets ) + datasets_allow_action_map = self.allow_action_on_items( trans, user_roles, self.permitted_actions.DATASET_ACCESS, datasets ) + can_access = {} + for dataset in datasets: + can_access[ dataset.id ] = datasets_public_map[ dataset.id ] or datasets_allow_action_map[ dataset.id ] + return can_access + + def can_access_dataset( self, user_roles, dataset ): + # SM: dataset_is_public will access dataset.actions, which is a + # backref that causes a query to be made to DatasetPermissions + retval = self.dataset_is_public( dataset ) or self.allow_action( user_roles, self.permitted_actions.DATASET_ACCESS, dataset ) + return retval + def can_manage_dataset( self, roles, dataset ): return self.allow_action( roles, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset ) def can_access_library( self, roles, library ): @@ -317,9 +464,14 @@ return self.allow_action( roles, self.permitted_actions.LIBRARY_MODIFY, item ) def can_manage_library_item( self, roles, item ): return self.allow_action( roles, self.permitted_actions.LIBRARY_MANAGE, item ) + def get_item_actions( self, action, item ): # item must be one of: Dataset, Library, LibraryFolder, LibraryDataset, LibraryDatasetDatasetAssociation + # SM: Accessing item.actions emits a query to Library_Dataset_Permissions + # if the item is a LibraryDataset: + # TODO: Pass in the item's actions - the item isn't needed return [ permission for permission in item.actions if permission.action == action.action ] + def guess_derived_permissions_for_datasets( self, datasets=[] ): """Returns a dict of { action : [ role, role, ... ] } for the output dataset based upon provided datasets""" perms = {} @@ -667,10 +819,55 @@ dataset = library_dataset.library_dataset_dataset_association.dataset if not dataset.purged and not self.dataset_is_public( dataset ): self.make_dataset_public( dataset ) + def dataset_is_public( self, dataset ): # A dataset is considered public if there are no "access" actions associated with it. Any # other actions ( 'manage permissions', 'edit metadata' ) are irrelevant. + # SM: Accessing dataset.actions will cause a query to be emitted. return self.permitted_actions.DATASET_ACCESS.action not in [ a.action for a in dataset.actions ] + + def datasets_are_public( self, trans, datasets ): + ''' + Given a transaction object and a list of Datasets, return + a mapping from Dataset ids to whether the Dataset is public + or not. All Dataset ids should be returned in the mapping's keys. + ''' + # We go the other way around from dataset_is_public: we start with + # all datasets being marked as public. If there is an access action + # associated with the dataset, then we mark it as nonpublic: + datasets_public = {} + dataset_ids = [ dataset.id for dataset in datasets ] + for dataset_id in dataset_ids: + datasets_public[ dataset_id ] = True + + # Now get all datasets which have DATASET_ACCESS actions: + access_data_perms = trans.sa_session.query( trans.app.model.DatasetPermissions ) \ + .filter( and_( trans.app.model.DatasetPermissions.dataset_id in dataset_ids, + trans.app.model.DatasetPermissions.action == self.permitted_actions.DATASET_ACCESS.action ) ) \ + .all() + + # Every dataset returned has "access" privileges associated with it, + # so it's not public. + for permission in access_data_perms: + datasets_public[ permission.dataset_id ] = False + + # Test code: Check if the results match up with the original: + test_code = False + if test_code: + log.debug( "datasets_are_public test: check datasets_are_public matches dataset_is_public:" ) + test_success = True + for dataset in datasets: + orig_is_public = self.dataset_is_public( dataset ) + if orig_is_public == datasets_public[ dataset.id ]: + log.debug( "\tMatch for dataset %d" % dataset.id ) + else: + success = False + log.error( "\tERROR: Did not match: single is_public: %s; multiple is_public: %s" + % ( single_is_public, datasets_public[ dataset.id ] ) ) + log.debug( "datasets_are_public: test succeeded? %s" % test_success ) + return datasets_public + + def make_dataset_public( self, dataset ): # A dataset is considered public if there are no "access" actions associated with it. Any # other actions ( 'manage permissions', 'edit metadata' ) are irrelevant. @@ -707,7 +904,7 @@ # Ensure that roles being associated with DATASET_ACCESS are a subset of the legitimate roles # derived from the roles associated with the access permission on item if it's not public. This # will keep ill-legitimate roles from being associated with the DATASET_ACCESS permission on the - # dataset (i.e., in the case where item is a library, if Role1 is associated with LIBRARY_ACCESS, + # dataset (i.e., in the case where item is .a library, if Role1 is associated with LIBRARY_ACCESS, # then only those users that have Role1 should be associated with DATASET_ACCESS. legitimate_roles = self.get_legitimate_roles( trans, item, cntrller ) ill_legitimate_roles = [] @@ -944,12 +1141,21 @@ if self.can_add_library_item( roles, folder ): return True, '' action = self.permitted_actions.DATASET_ACCESS + + # SM: TODO: This is for timing debug. Delete it later. + from datetime import datetime, timedelta + query_start = datetime.now() lddas = self.sa_session.query( self.model.LibraryDatasetDatasetAssociation ) \ .join( "library_dataset" ) \ .filter( self.model.LibraryDataset.folder == folder ) \ .join( "dataset" ) \ .options( eagerload_all( "dataset.actions" ) ) \ .all() + query_end = datetime.now() + query_delta = query_end - query_start + #log.debug( "Check folder contents: join query time: %d.%.6d sec" % + # ( query_delta.seconds, query_delta.microseconds ) ) + for ldda in lddas: ldda_access_permissions = self.get_item_actions( action, ldda.dataset ) if not ldda_access_permissions: diff -r 4f788268c76dcb77e99076dbfbc8cf65c05b00cc -r a995f7aca75c03bd180689aead6a8870928d8fdf lib/galaxy/web/controllers/library_common.py --- a/lib/galaxy/web/controllers/library_common.py +++ b/lib/galaxy/web/controllers/library_common.py @@ -92,6 +92,65 @@ #"force_history_refresh": force_history_refresh } return rval + + def display_folder_hierarchy( self, trans_in, cntrller_in, use_panels_in, library_in, created_ldda_ids_in, hidden_folder_ids_in, show_deleted_in, comptypes_in, current_user_roles_in, message_in, status ): + log.debug( "display_folder_hierarchy: enter" ) + # SM: Determine if this is an admin user (trans.user_is_admin and cntrller + # is library_admin). If so, then the user gets add, modify, manage permissions. + # Otherwise, if this is the library/requests controller then lookup those + # three permissions. Otherwise, it's all not true. + # + # Now get the library's info_assocation (?). TODO: Look into this. + # Determine if there are accessible folders: + # 1. Is the root folder accessible to this user? + # 2. IS the root folder accessible to this user (without looking downward)? + # 3. Is this guy an admin OR is the root folder accessible to this user? + # Evidently roles are used each time. + # + # Then show management buttons and links, and then call render_folders + + # render_folder: + # Repeat determining if this is an admin user: + # is_admin = trans.user_is_admin() and cntrller == 'library_admin' + # Turn the list of created lddas into a list - split by comma, turn one + # element into its own list, and leave lists alone. + # (Christ) If this is an admin user, then all permissions are granted. + # O.w., if this is the library controller, + # determine the folder ids along with whether the folders can be + # shown, and determine the other three admin permissions. + # O.w., no modifications are allowed - it's view-only. + # Get the form type and (sigh) again get the folder's info_association (?) + # Display information about the folder. + # Get the list of deleted (if shown) and non-deleted datasets and subfolders. + # Call render_folder for every subfolder + # For every library dataset, determine the ldda. If there is an ldda, then + # determine if the ldda's dataset can be displayed. + + # TODO: Move this to a security function/method later. + is_admin = trans.user_is_admin() and cntrller == 'library_admin' + + if is_admin: + can_add = can_modify = can_manage = True + elif cntrller in [ 'library', 'requests' ]: + can_add = trans.app.security_agent.can_add_library_item( current_user_roles_in, library ) + can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles_in, library ) + can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles_in, library ) + else: + can_add = can_modify = can_manage = False + + info_association, inherited = library_in.get_info_association() + log.debug( "type(info_association): %s" % type(info_association) ) + log.debug( "type(inherited): %s" % type(inherited) ) + + # SM: determine if folders are accessible underneath here: + has_accessible_folders = is_admin or trans.app.security_agent.has_accessible_folders( trans, library.root_folder, trans.user, current_user_roles_in ) + if has_accessible_folders and cntrller in ['library', 'requests']: + self.display_folder( 'library', library.root_folder, 0, created_ldda_ids, library, hidden_folder_ids, tracked_datasets, show_deleted = show_deleted, parent=None, root_folder=True, simple=simple ) + elif ( trans_in.user_is_admin() and cntrller in [ 'library_admin', 'requests_admin' ] ): + # SM: TODO: Start here. + pass + pass + @web.expose def browse_library( self, trans, cntrller, **kwd ): params = util.Params( kwd ) @@ -128,22 +187,36 @@ message += "message \"This job is running\" is cleared from the \"Information\" column below for each selected dataset." status = "info" comptypes = get_comptypes( trans ) - try: - return trans.fill_template( '/library/common/browse_library.mako', - cntrller=cntrller, - use_panels=use_panels, - library=library, - created_ldda_ids=created_ldda_ids, - hidden_folder_ids=hidden_folder_ids, - show_deleted=show_deleted, - comptypes=comptypes, - current_user_roles=current_user_roles, - message=message, - status=status ) - except Exception, e: - message = 'Error attempting to display contents of library (%s): %s.' % ( str( library.name ), str( e ) ) - status = 'error' + # SM: Retrieve the entire hierarchy and pass it to the template to fill in. + # We'll eliminate the mako from retrieving stuff for now. + # SM: TODO: Add configuration item + # if trans.app.config.optimize_folders: + if False: + hierarchy = self.display_folder_hierarchy( trans, cntrller, use_panels, library, + created_ldda_ids, hidden_folder_ids, + show_deleted, comptypes, + current_user_roles, message, + status ) + + else: + try: + # SM: TODO: Add configuration variable asap. + return trans.fill_template( '/library/common/browse_library.mako', + cntrller=cntrller, + use_panels=use_panels, + library=library, + created_ldda_ids=created_ldda_ids, + hidden_folder_ids=hidden_folder_ids, + show_deleted=show_deleted, + comptypes=comptypes, + current_user_roles=current_user_roles, + message=message, + status=status ) + except Exception, e: + message = 'Error attempting to display contents of library (%s): %s.' % ( str( library.name ), str( e ) ) + status = 'error' default_action = params.get( 'default_action', None ) + return trans.response.send_redirect( web.url_for( use_panels=use_panels, controller=cntrller, action='browse_libraries', @@ -2534,16 +2607,57 @@ .options( eagerload_all( "actions" ) ) \ .order_by( trans.app.model.LibraryFolder.table.c.name ) \ .all() + +def map_library_datasets_to_lddas( trans, lib_datasets ): + ''' + Given a list of LibraryDatasets, return a map from the LibraryDatasets + to their LDDAs. If an LDDA does not exist for a LibraryDataset, then + there will be no entry in the return hash. + ''' + # Get a list of the LibraryDatasets' ids so that we can pass it along to + # a query to retrieve the LDDAs. This eliminates querying for each + # LibraryDataset. + lib_dataset_ids = [ x.library_dataset_dataset_association_id for x in lib_datasets ] + lddas = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ) \ + .filter( trans.app.model.LibraryDatasetDatasetAssociation.id.in_( lib_dataset_ids ) ) \ + .all() + + # Map the LibraryDataset to the returned LDDAs: + ret_lddas = {} + for ldda in lddas: + ret_lddas[ldda.library_dataset_id] = ldda + return ret_lddas + +def datasets_for_lddas( trans, lddas ): + ''' + Given a list of LDDAs, return a list of Datasets for them. + ''' + dataset_ids = [ x.dataset_id for x in lddas ] + datasets = trans.sa_session.query( trans.app.model.Dataset ) \ + .filter( trans.app.model.Dataset.id.in_( dataset_ids ) ) \ + .all() + return datasets + def active_folders_and_library_datasets( trans, folder ): + # SM: TODO: Eliminate timing code + from datetime import datetime, timedelta + query_start = datetime.now() folders = active_folders( trans, folder ) library_datasets = trans.sa_session.query( trans.model.LibraryDataset ) \ .filter( and_( trans.model.LibraryDataset.table.c.deleted == False, trans.model.LibraryDataset.table.c.folder_id == folder.id ) ) \ .order_by( trans.model.LibraryDataset.table.c._name ) \ .all() + query_end = datetime.now() + query_delta = query_end - query_start + #log.debug( "active_folders_and_library_datasets: %d.%.6d" % + # ( query_delta.seconds, query_delta.microseconds ) ) return folders, library_datasets + def activatable_folders_and_library_datasets( trans, folder ): folders = activatable_folders( trans, folder ) + from datetime import datetime, timedelta + query_start = datetime.now() library_datasets = trans.sa_session.query( trans.model.LibraryDataset ) \ .filter( trans.model.LibraryDataset.table.c.folder_id == folder.id ) \ .join( ( trans.model.LibraryDatasetDatasetAssociation.table, @@ -2553,6 +2667,10 @@ .filter( trans.model.Dataset.table.c.deleted == False ) \ .order_by( trans.model.LibraryDataset.table.c._name ) \ .all() + query_end = datetime.now() + query_delta = query_end - query_start + log.debug( "activatable_folders_and_library_datasets: %d.%.6d" % + ( query_delta.seconds, query_delta.microseconds ) ) return folders, library_datasets def branch_deleted( folder ): # Return True if a folder belongs to a branch that has been deleted diff -r 4f788268c76dcb77e99076dbfbc8cf65c05b00cc -r a995f7aca75c03bd180689aead6a8870928d8fdf templates/library/common/browse_library_opt.mako --- /dev/null +++ b/templates/library/common/browse_library_opt.mako @@ -0,0 +1,630 @@ +<%namespace file="/message.mako" import="render_msg" /> +<%namespace file="/library/common/library_item_info.mako" import="render_library_item_info" /> +<%namespace file="/library/common/common.mako" import="render_actions_on_multiple_items" /> +<%namespace file="/library/common/common.mako" import="render_compression_types_help" /> +<%namespace file="/library/common/common.mako" import="common_javascripts" /> + +<%! + def inherit(context): + if context.get('use_panels'): + return '/webapps/galaxy/base_panels.mako' + else: + return '/base.mako' +%> +<%inherit file="${inherit(context)}"/> + +<%def name="init()"> +<% + self.has_left_panel=False + self.has_right_panel=False + self.message_box_visible=False + self.active_view="user" + self.overlay_visible=False + self.has_accessible_datasets = False +%> +</%def> + +## +## Override methods from base.mako and base_panels.mako +## +<%def name="center_panel()"> + <div style="overflow: auto; height: 100%;"> + <div class="page-container" style="padding: 10px;"> + ${render_content()} + </div> + </div> +</%def> + +## Render the grid's basic elements. Each of these elements can be subclassed. +<%def name="body()"> + ${render_content()} +</%def> + +<%def name="title()">Browse data library</%def> +<%def name="stylesheets()"> + ${parent.stylesheets()} + ${h.css( "library" )} +</%def> + +<%def name="javascripts()"> + ${parent.javascripts()} + ${h.js("libs/json2")} + ${h.js("libs/jquery/jstorage")} + ${common_javascripts()} + ${self.grid_javascripts()} +</%def> + +<%def name="grid_javascripts()"> + <script type="text/javascript"> + var init_libraries = function() { + var storage_id = "library-expand-state-${trans.security.encode_id(library.id)}"; + + var restore_folder_state = function() { + var state = $.jStorage.get(storage_id); + if (state) { + for (var id in state) { + if (state[id] === true) { + var row = $("#" + id), + index = row.parent().children().index(row); + row.addClass("expanded").show(); + row.siblings().filter("tr[parent='" + index + "']").show(); + } + } + } + }; + + var save_folder_state = function() { + var state = {}; + $("tr.folderRow").each( function() { + var folder = $(this); + state[folder.attr("id")] = folder.hasClass("expanded"); + }); + $.jStorage.set(storage_id, state); + }; + + $("#library-grid").each(function() { + var child_of_parent_cache = {}; + // Recursively fill in children and descendents of each row + var process_row = function(q, parents) { + // Find my index + var parent = q.parent(), + this_level = child_of_parent_cache[parent] || (child_of_parent_cache[parent] = parent.children()); + + var index = this_level.index(q); + // Find my immediate children + var children = $(par_child_dict[index]); + // Recursively handle them + var descendents = children; + children.each( function() { + child_descendents = process_row( $(this), parents.add(q) ); + descendents = descendents.add(child_descendents); + }); + // Set up expand / hide link + var expand_fn = function() { + if ( q.hasClass("expanded") ) { + descendents.hide(); + descendents.removeClass("expanded"); + q.removeClass("expanded"); + } else { + children.show(); + q.addClass("expanded"); + } + save_folder_state(); + }; + $("." + q.attr("id") + "-click").click(expand_fn); + // Check/uncheck boxes in subfolders. + q.children("td").children("input[type=checkbox]").click( function() { + if ( $(this).is(":checked") ) { + descendents.find("input[type=checkbox]").attr("checked", true); + } else { + descendents.find("input[type=checkbox]").attr("checked", false); + // If you uncheck a lower level checkbox, uncheck the boxes above it + // (since deselecting a child means the parent is not fully selected any more). + parents.children("td").children("input[type=checkbox]").attr("checked", false); + } + }); + // return descendents for use by parent + return descendents; + } + + // Initialize dict[parent_id] = rows_which_have_that_parent_id_as_parent_attr + var par_child_dict = {}, + no_parent = []; + + $(this).find("tbody tr").each( function() { + if ( $(this).attr("parent")) { + var parent = $(this).attr("parent"); + if (par_child_dict[parent] !== undefined) { + par_child_dict[parent].push(this); + } else { + par_child_dict[parent] = [this]; + } + } else { + no_parent.push(this); + } + }); + + $(no_parent).each( function() { + descendents = process_row( $(this), $([]) ); + descendents.hide(); + }); + }); + + restore_folder_state(); + }; + $(function() { + init_libraries(); + }); + + // Looks for changes in dataset state using an async request. Keeps + // calling itself (via setTimeout) until all datasets are in a terminal + // state. + var updater = function ( tracked_datasets ) { + // Check if there are any items left to track + var empty = true; + for ( i in tracked_datasets ) { + empty = false; + break; + } + if ( ! empty ) { + setTimeout( function() { updater_callback( tracked_datasets ) }, 3000 ); + } + }; + var updater_callback = function ( tracked_datasets ) { + // Build request data + var ids = [] + var states = [] + $.each( tracked_datasets, function ( id, state ) { + ids.push( id ); + states.push( state ); + }); + // Make ajax call + $.ajax( { + type: "POST", + url: "${h.url_for( controller='library_common', action='library_item_updates' )}", + dataType: "json", + data: { ids: ids.join( "," ), states: states.join( "," ) }, + success : function ( data ) { + $.each( data, function( id, val ) { + // Replace HTML + var cell = $("#libraryItem-" + id).find("#libraryItemInfo"); + cell.html( val.html ); + // If new state was terminal, stop tracking + if (( val.state == "ok") || ( val.state == "error") || ( val.state == "empty") || ( val.state == "deleted" ) || ( val.state == "discarded" )) { + delete tracked_datasets[ parseInt(id) ]; + } else { + tracked_datasets[ parseInt(id) ] = val.state; + } + }); + updater( tracked_datasets ); + }, + error: function() { + // Just retry, like the old method, should try to be smarter + updater( tracked_datasets ); + } + }); + }; + </script> +</%def> + +<%def name="render_dataset( cntrller, ldda, library_dataset, selected, library, folder, pad, parent, row_counter, tracked_datasets, show_deleted=False, simple=False )"> + <% + ## The received ldda must always be a LibraryDatasetDatasetAssociation object. The object id passed to methods + ## from the drop down menu should be the ldda id to prevent id collision ( which could happen when displaying + ## children, which are always lddas ). We also need to make sure we're displaying the latest version of this + ## library_dataset, so we display the attributes from the ldda. + + from galaxy.web.controllers.library_common import branch_deleted + # SM: DELETEME + import logging + log = logging.getLogger( __name__ ) + + is_admin = trans.user_is_admin() and cntrller == 'library_admin' + + if ldda == library_dataset.library_dataset_dataset_association: + current_version = True + if is_admin: + can_modify = can_manage = True + elif cntrller in [ 'library', 'requests' ]: + #log.debug( "SM: Query 4: backref to Library_Dataset_Permissions" ) + can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, library_dataset ) + can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, library_dataset ) + else: + can_modify = can_manage = False + else: + current_version = False + if current_version and ldda.state not in ( 'ok', 'error', 'empty', 'deleted', 'discarded' ): + tracked_datasets[ldda.id] = ldda.state + #log.debug( "SM: Query 5 call: LibraryDatasetDatasetInfoAssocation" ) + info_association, inherited = ldda.get_info_association( restrict=True ) + # SM: The form type is for displaying URLs only; ignore for now. + form_type = trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE + %> + %if current_version and ( not ldda.library_dataset.deleted or show_deleted ): + <tr class="datasetRow" + %if parent is not None: + parent="${parent}" + %endif + id="libraryItem-${ldda.id}"> + <td style="padding-left: ${pad+20}px;"> + <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id )}" value="${trans.security.encode_id( ldda.id )}" + %if selected: + checked="checked" + %endif + /> + %if simple: + <label for="${trans.security.encode_id( ldda.id )}">${ util.unicodify( ldda.name )}</label> + %else: + <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id}-popup"> + <a class="view-info" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}"> + %if ldda.library_dataset.deleted: + <div class="libraryItem-error">${util.unicodify( ldda.name )}</div> + %else: + ${util.unicodify( ldda.name )} + %endif + </a> + </div> + %if not library.deleted: + <div popupmenu="dataset-${ldda.id}-popup"> + %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify: + <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a> + <a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='ldda', item_id=trans.security.encode_id( ldda.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this dataset</a> + %else: + <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">View information</a> + %endif + %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify and not info_association: + <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='ldda', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Use template</a> + %endif + %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify and info_association: + <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='ldda', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit template</a> + <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='ldda', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Unuse template</a> + %endif + %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_manage: + %if not trans.app.security_agent.dataset_is_public( ldda.dataset ): + <a class="action-button" href="${h.url_for( controller='library_common', action='make_library_item_public', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_type='ldda', id=trans.security.encode_id( ldda.dataset.id ), use_panels=use_panels, show_deleted=show_deleted )}">Make public</a> + %endif + <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_permissions', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit permissions</a> + %endif + %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify: + <a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), replace_id=trans.security.encode_id( library_dataset.id ), show_deleted=show_deleted )}">Upload a new version of this dataset</a> + %endif + %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ldda.has_data: + <a class="action-button" href="${h.url_for( controller='library_common', action='import_datasets_to_histories', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), ldda_ids=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Import this dataset into selected histories</a> + <a class="action-button" href="${h.url_for( controller='library_common', action='download_dataset_from_folder', cntrller=cntrller, id=trans.security.encode_id( ldda.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels )}">Download this dataset</a> + %endif + %if can_modify: + %if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted: + <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name )}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a> + %elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted: + <a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a> + %endif + %endif + </div> + %endif + %endif + </td> + % if not simple: + <td id="libraryItemInfo">${render_library_item_info( ldda )}</td> + <td>${ldda.extension}</td> + % endif + <td>${ldda.create_time.strftime( "%Y-%m-%d" )}</td> + <td>${ldda.get_size( nice_size=True )}</td> + </tr> + <% + my_row = row_counter.count + row_counter.increment() + %> + %endif +</%def> + +<%def name="format_delta( tdelta )"> + <% + from datetime import datetime + return "%d.%.6d" % ( tdelta.seconds, tdelta.microseconds ) + %> +</%def> + +<%def name="render_folder( cntrller, folder, folder_pad, created_ldda_ids, library, hidden_folder_ids, tracked_datasets, show_deleted=False, parent=None, row_counter=None, root_folder=False, simple=False )"> + <% + from galaxy.web.controllers.library_common import active_folders, active_folders_and_library_datasets, activatable_folders_and_library_datasets, map_library_datasets_to_lddas, branch_deleted, datasets_for_lddas + + # SM: DELETEME + from datetime import datetime, timedelta + import logging + log = logging.getLogger( __name__ ) + + is_admin = trans.user_is_admin() and cntrller == 'library_admin' + has_accessible_library_datasets = trans.app.security_agent.has_accessible_library_datasets( trans, folder, trans.user, current_user_roles, search_downward=False ) + + if root_folder: + pad = folder_pad + expander = h.url_for("/static/images/silk/resultset_bottom.png") + folder_img = h.url_for("/static/images/silk/folder_page.png") + else: + pad = folder_pad + 20 + expander = h.url_for("/static/images/silk/resultset_next.png") + folder_img = h.url_for("/static/images/silk/folder.png") + # SM: If this is a comma-delimited list of LDDAs, then split them up + # into a list. For anything else, turn created_ldda_ids into a single + # item list. + if created_ldda_ids: + created_ldda_ids = util.listify( created_ldda_ids ) + if str( folder.id ) in hidden_folder_ids: + return "" + my_row = None + if is_admin: + can_add = can_modify = can_manage = True + elif cntrller in [ 'library' ]: + can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, folder ) + if not can_access: + can_show, folder_ids = \ + trans.app.security_agent.show_library_item( trans.user, + current_user_roles, + folder, + [ trans.app.security_agent.permitted_actions.LIBRARY_ADD, + trans.app.security_agent.permitted_actions.LIBRARY_MODIFY, + trans.app.security_agent.permitted_actions.LIBRARY_MANAGE ] ) + if not can_show: + return "" + can_add = trans.app.security_agent.can_add_library_item( current_user_roles, folder ) + can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, folder ) + can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, folder ) + else: + can_add = can_modify = can_manage = False + + form_type = trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE + info_association, inherited = folder.get_info_association( restrict=True ) + %> + %if not root_folder and ( not folder.deleted or show_deleted ): + <% encoded_id = trans.security.encode_id( folder.id ) %> + <tr id="folder-${encoded_id}" class="folderRow libraryOrFolderRow" + %if parent is not None: + parent="${parent}" + style="display: none;" + %endif + > + <td style="padding-left: ${folder_pad}px;"> + <input type="checkbox" class="folderCheckbox"/> + <span class="expandLink folder-${encoded_id}-click"> + <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup"> + <a class="folder-${encoded_id}-click" href="javascript:void(0);"> + <span class="rowIcon"></span> + %if folder.deleted: + <div class="libraryItem-error">${folder.name}</div> + %else: + ${folder.name} + %endif + </a> + </div> + </span> + %if not library.deleted: + <div popupmenu="folder_img-${folder.id}-popup"> + %if not branch_deleted( folder ) and can_add: + <a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a> + <a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add sub-folder</a> + %endif + %if not branch_deleted( folder ): + %if has_accessible_library_datasets: + <a class="action-button" href="${h.url_for( controller='library_common', action='import_datasets_to_histories', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Select datasets for import into selected histories</a> + %endif + %if can_modify: + <a class="action-button" href="${h.url_for( controller='library_common', action='folder_info', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a> + <a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='folder', item_id=trans.security.encode_id( folder.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this folder</a> + %else: + <a class="action-button" class="view-info" href="${h.url_for( controller='library_common', action='folder_info', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">View information</a> + %endif + %endif + %if not branch_deleted( folder ) and can_modify and not info_association: + <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='folder', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Use template</a> + %endif + %if not branch_deleted( folder ) and can_modify and info_association: + <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='folder', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit template</a> + <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='folder', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Unuse template</a> + %endif + %if not branch_deleted( folder ) and can_manage: + %if not trans.app.security_agent.folder_is_public( folder ): + <a class="action-button" href="${h.url_for( controller='library_common', action='make_library_item_public', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_type='folder', id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Make public</a> + %endif + <a class="action-button" href="${h.url_for( controller='library_common', action='folder_permissions', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit permissions</a> + %endif + %if can_modify: + %if not library.deleted and not folder.deleted: + <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a> + %elif not library.deleted and folder.deleted and not folder.purged: + <a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a> + %endif + %endif + </div> + %endif + <td> + %if folder.description: + ${folder.description} + %endif + <td colspan="3"></td> + </tr> + <% + my_row = row_counter.count + row_counter.increment() + %> + %endif + <% + # TODO: If show_deleted is set to True, then nothing is displayed. Why? This wasn't the case + # in the past. + if show_deleted: + sub_folders, library_datasets = activatable_folders_and_library_datasets( trans, folder ) + else: + sub_folders, library_datasets = active_folders_and_library_datasets( trans, folder ) + # Render all the subfolders: + # TODO: Check permissions first. + for sub_folder in sub_folders: + render_folder( cntrller, sub_folder, pad, created_ldda_ids, library, [], tracked_datasets, show_deleted=show_deleted, parent=my_row, row_counter=row_counter, root_folder=False ) + + # Map LibraryDatasets to LDDAs, then map LDDAs to Datasets. + # Then determine which Datasets are accessible and which are not. + # For every LibraryDataset, if there's an LDDA for it and it's + # accessible then display it. + if ( len( library_datasets ) > 0 ): + lib_dataset_ldda_map = map_library_datasets_to_lddas( trans, library_datasets ) + dataset_list = datasets_for_lddas( trans, lib_dataset_ldda_map.values() ) + can_access_datasets = trans.app.security_agent.get_dataset_access_mapping( trans, current_user_roles, dataset_list ) + for library_dataset in library_datasets: + ldda = lib_dataset_ldda_map[ library_dataset.id ] + if ldda: + can_access = is_admin or can_access_datasets[ ldda.dataset_id ] + selected = created_ldda_ids and str( ldda.id ) in created_ldda_ids + if can_access: + render_dataset( cntrller, ldda, library_dataset, selected, library, folder, pad, my_row, row_counter, tracked_datasets, show_deleted=show_deleted ) + %> +</%def> + +<%def name="render_content(simple=False)"> + <% + from galaxy import util + from galaxy.web.controllers.library_common import branch_deleted + from time import strftime + import logging + log = logging.getLogger( __name__ ) + + is_admin = trans.user_is_admin() and cntrller == 'library_admin' + + if is_admin: + can_add = can_modify = can_manage = True + elif cntrller in [ 'library', 'requests' ]: + can_add = trans.app.security_agent.can_add_library_item( current_user_roles, library ) + can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, library ) + can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, library ) + else: + can_add = can_modify = can_manage = False + + info_association, inherited = library.get_info_association() + form_type = trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE + + # SM: These are mostly display-specific; ignore them for now. + # The has_accessible_folders determines if anything can be shown - use it. + self.has_accessible_datasets = trans.app.security_agent.has_accessible_library_datasets( trans, library.root_folder, trans.user, current_user_roles ) + root_folder_has_accessible_library_datasets = trans.app.security_agent.has_accessible_library_datasets( trans, library.root_folder, trans.user, current_user_roles, search_downward=False ) + has_accessible_folders = is_admin or trans.app.security_agent.has_accessible_folders( trans, library.root_folder, trans.user, current_user_roles ) + + tracked_datasets = {} + + class RowCounter( object ): + def __init__( self ): + self.count = 0 + def increment( self ): + self.count += 1 + def __str__( self ): + return str( self.count ) + %> + + <h2>Data Library “${library.name}”</h2> + + <ul class="manage-table-actions"> + %if not library.deleted and ( is_admin or can_add ): + <li><a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( library.root_folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a></li> + <li><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( library.root_folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add folder</a></li> + %endif + %if ( ( not library.deleted ) and ( can_modify or can_manage ) ) or ( can_modify and not library.purged ) or ( library.purged ): + <li><a class="action-button" id="library-${library.id}-popup" class="menubutton">Library Actions</a></li> + <div popupmenu="library-${library.id}-popup"> + %if not library.deleted: + %if can_modify: + <a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a> + <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a> + %if show_deleted: + <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=False )}">Hide deleted items</a> + %else: + <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=True )}">Show deleted items</a> + %endif + %endif + %if can_modify and not library.info_association: + <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='library', form_type=form_type, library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Use template</a> + %endif + %if can_modify and info_association: + <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='library', form_type=form_type, library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit template</a> + <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='library', form_type=form_type, library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Unuse template</a> + %endif + %if can_manage: + %if not trans.app.security_agent.library_is_public( library, contents=True ): + <a class="action-button" href="${h.url_for( controller='library_common', action='make_library_item_public', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_type='library', id=trans.security.encode_id( library.id ), contents=True, use_panels=use_panels, show_deleted=show_deleted )}">Make public</a> + %endif + <a class="action-button" href="${h.url_for( controller='library_common', action='library_permissions', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit permissions</a> + %endif + %if root_folder_has_accessible_library_datasets: + <a class="action-button" href="${h.url_for( controller='library_common', action='import_datasets_to_histories', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( library.root_folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Select datasets for import into selected histories</a> + %endif + %elif can_modify and not library.purged: + <a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library', use_panels=use_panels )}">Undelete this data library</a> + %elif library.purged: + <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">This data library has been purged</a> + %endif + </div> + %endif + </ul> + + %if message: + ${render_msg( message, status )} + %endif + + %if library.synopsis not in [ '', 'None', None ]: + <div class="libraryItemBody"> + ${library.synopsis} + </div> + %endif + + %if self.has_accessible_datasets: + <form name="act_on_multiple_datasets" action="${h.url_for( controller='library_common', action='act_on_multiple_datasets', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}" onSubmit="javascript:return checkForm();" method="post"> + %endif + %if has_accessible_folders: + <table cellspacing="0" cellpadding="0" border="0" width="100%" class="grid" id="library-grid"> + <thead> + <tr class="libraryTitle"> + <th> + %if self.has_accessible_datasets: + <input type="checkbox" id="checkAll" name=select_all_datasets_checkbox value="true" onclick='checkAllFields(1);'/><input type="hidden" name=select_all_datasets_checkbox value="true"/> + %endif + Name + </th> + % if not simple: + <th>Message</th> + <th>Data type</th> + % endif + <th>Date uploaded</th> + <th>File size</th> + </tr> + </thead> + <% row_counter = RowCounter() %> + ## SM: Here is where we render the libraries based on admin/non-admin privileges: + %if cntrller in [ 'library', 'requests' ]: + ${self.render_folder( 'library', library.root_folder, 0, created_ldda_ids, library, hidden_folder_ids, tracked_datasets, show_deleted=show_deleted, parent=None, row_counter=row_counter, root_folder=True, simple=simple )} + ## SM: TODO: WTF? + %if not library.deleted and self.has_accessible_datasets and not simple: + ${render_actions_on_multiple_items()} + %endif + %elif ( trans.user_is_admin() and cntrller in [ 'library_admin', 'requests_admin' ] ): + ${self.render_folder( 'library_admin', library.root_folder, 0, created_ldda_ids, library, [], tracked_datasets, show_deleted=show_deleted, parent=None, row_counter=row_counter, root_folder=True )} + ## SM: TODO: WTF? + %if not library.deleted and not show_deleted and self.has_accessible_datasets: + ${render_actions_on_multiple_items()} + %endif + %endif + </table> + %endif + %if self.has_accessible_datasets: + </form> + %endif + + %if tracked_datasets: + <script type="text/javascript"> + // Updater + updater({${ ",".join( [ '"%s" : "%s"' % ( k, v ) for k, v in tracked_datasets.iteritems() ] ) }}); + </script> + <!-- running: do not change this comment, used by TwillTestCase.library_wait --> + %endif + + %if self.has_accessible_datasets and not simple: + ${render_compression_types_help( comptypes )} + %endif + %if not has_accessible_folders: + The data library '${library.name}' does not contain any datasets that you can access. + %endif +</%def> Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.