2 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/7114d15a6d22/ Changeset: 7114d15a6d22 Branch: next-stable User: jmchilton Date: 2014-12-06 01:35:22+00:00 Summary: Twill tool tests allowed selects specified by display text instead of value. This didn't work for API tests - restore this functionality and add test case in test/functional/tools/multi_select.xml to verify this behavior is correct. Affected #: 2 files diff -r 149784a152afc3d7164eaf7dfa7d5dc97be92671 -r 7114d15a6d2234cb895d058e0e44c86f373c49b2 lib/galaxy/tools/test.py --- a/lib/galaxy/tools/test.py +++ b/lib/galaxy/tools/test.py @@ -196,12 +196,37 @@ if not isinstance(param_value, list): param_value = [ param_value ] processed_value = [ self.__add_uploaded_dataset( context.for_state(), v, param_extra, value ) for v in param_value ] - if isinstance( value, basic.DataCollectionToolParameter ): + elif isinstance( value, basic.DataCollectionToolParameter ): assert 'collection' in param_extra collection_def = param_extra[ 'collection' ] for ( name, value, extra ) in collection_def.collect_inputs(): require_file( name, value, extra, self.required_files ) processed_value = collection_def + elif isinstance( value, basic.SelectToolParameter ): + # Tests may specify values as either raw value or the value + # as they appear in the list - the API doesn't and shouldn't + # accept the text value - so we need to convert the text + # into the form value. + def process_param_value( param_value ): + found_value = False + value_for_text = None + if value.static_options: + for (text, opt_value, selected) in value.static_options: + if param_value == opt_value: + found_value = True + if value_for_text is None and param_value == text: + value_for_text = opt_value + if not found_value and value_for_text is not None: + processed_value = value_for_text + else: + processed_value = param_value + return processed_value + # Do replacement described above for lists or singleton + # values. + if isinstance( param_value, list ): + processed_value = map( process_param_value, param_value ) + else: + processed_value = process_param_value( param_value ) else: processed_value = param_value expanded_inputs[ context.for_state() ] = processed_value diff -r 149784a152afc3d7164eaf7dfa7d5dc97be92671 -r 7114d15a6d2234cb895d058e0e44c86f373c49b2 test/functional/tools/multi_select.xml --- a/test/functional/tools/multi_select.xml +++ b/test/functional/tools/multi_select.xml @@ -25,5 +25,13 @@ </assert_contents></output></test> + <test> + <param name="select_ex" value="Ex1" /> + <output name="output"> + <assert_contents> + <has_line line="--ex1" /> + </assert_contents> + </output> + </test></tests></tool> https://bitbucket.org/galaxy/galaxy-central/commits/719191cd0f3e/ Changeset: 719191cd0f3e User: jmchilton Date: 2014-12-06 01:36:48+00:00 Summary: Merge next-stable. Affected #: 31 files diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 client/galaxy/scripts/galaxy.library.js --- a/client/galaxy/scripts/galaxy.library.js +++ b/client/galaxy/scripts/galaxy.library.js @@ -36,7 +36,7 @@ initialize: function() { this.routesHit = 0; //keep count of number of routes handled by the application - Backbone.history.on('route', function() { this.routesHit++; }, this); + Backbone.history.on( 'route', function() { this.routesHit++; }, this ); }, routes: { @@ -45,6 +45,7 @@ "library/:library_id/permissions" : "library_permissions", "folders/:folder_id/permissions" : "folder_permissions", "folders/:id" : "folder_content", + "folders/:id/page/:show_page" : "folder_page", "folders/:folder_id/datasets/:dataset_id" : "dataset_detail", "folders/:folder_id/datasets/:dataset_id/permissions" : "dataset_permissions", "folders/:folder_id/datasets/:dataset_id/versions/:ldda_id" : "dataset_version", @@ -53,13 +54,13 @@ }, back: function() { - if(this.routesHit > 1) { + if( this.routesHit > 1 ) { //more than one route hit -> user did not land to current page directly window.history.back(); } else { //otherwise go to the home page. Use replaceState if available so //the navigation doesn't create an extra history entry - this.navigate('#', {trigger:true, replace:true}); + this.navigate( '#', { trigger:true, replace:true } ); } } }); @@ -71,7 +72,8 @@ with_deleted : false, sort_order : 'asc', sort_by : 'name', - library_page_size : 20 + library_page_size : 20, + folder_page_size : 15 } }); @@ -94,10 +96,10 @@ this.library_router = new LibraryRouter(); - this.library_router.on('route:libraries', function() { - Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView(); - Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView(); - }); + this.library_router.on( 'route:libraries', function() { + Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView(); + Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView(); + }); this.library_router.on('route:libraries_page', function( show_page ) { if ( Galaxy.libraries.libraryToolbarView === null ){ @@ -108,66 +110,77 @@ } }); - this.library_router.on('route:folder_content', function(id) { + this.library_router.on( 'route:folder_content', function( id ) { if (Galaxy.libraries.folderToolbarView){ - Galaxy.libraries.folderToolbarView.$el.unbind('click'); - } - Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView({id: id}); - Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView({id: id}); - }); + Galaxy.libraries.folderToolbarView.$el.unbind( 'click' ); + } + Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( { id: id } ); + Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: id } ); + }); - this.library_router.on('route:download', function(folder_id, format) { - if ($('#folder_list_body').find(':checked').length === 0) { - mod_toastr.info( 'You must select at least one dataset to download' ); - Galaxy.libraries.library_router.navigate('folders/' + folder_id, {trigger: true, replace: true}); - } else { - Galaxy.libraries.folderToolbarView.download(folder_id, format); - Galaxy.libraries.library_router.navigate('folders/' + folder_id, {trigger: false, replace: true}); - } - }); + this.library_router.on( 'route:folder_page', function( id, show_page ) { + if ( Galaxy.libraries.folderToolbarView === null ){ + Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( {id: id} ); + Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: id, show_page: show_page } ); + } else { + Galaxy.libraries.folderListView.render( { id: id, show_page: parseInt( show_page ) } ) + } + }); - this.library_router.on('route:dataset_detail', function(folder_id, dataset_id){ - if (Galaxy.libraries.datasetView){ - Galaxy.libraries.datasetView.$el.unbind('click'); - } - Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id}); - }); - this.library_router.on('route:dataset_version', function(folder_id, dataset_id, ldda_id){ - if (Galaxy.libraries.datasetView){ - Galaxy.libraries.datasetView.$el.unbind('click'); - } - Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, ldda_id: ldda_id, show_version: true}); - }); + this.library_router.on( 'route:download', function( folder_id, format ) { + if ( $( '#folder_list_body' ).find( ':checked' ).length === 0 ) { + mod_toastr.info( 'You must select at least one dataset to download' ); + Galaxy.libraries.library_router.navigate( 'folders/' + folder_id, { trigger: true, replace: true } ); + } else { + Galaxy.libraries.folderToolbarView.download( folder_id, format ); + Galaxy.libraries.library_router.navigate( 'folders/' + folder_id, { trigger: false, replace: true } ); + } + }); - this.library_router.on('route:dataset_permissions', function(folder_id, dataset_id){ - if (Galaxy.libraries.datasetView){ - Galaxy.libraries.datasetView.$el.unbind('click'); - } - Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, show_permissions: true}); - }); + this.library_router.on( 'route:dataset_detail', function(folder_id, dataset_id){ + if (Galaxy.libraries.datasetView){ + Galaxy.libraries.datasetView.$el.unbind('click'); + } + Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id}); + }); - this.library_router.on('route:library_permissions', function(library_id){ - if (Galaxy.libraries.libraryView){ - Galaxy.libraries.libraryView.$el.unbind('click'); - } - Galaxy.libraries.libraryView = new mod_library_library_view.LibraryView({id: library_id, show_permissions: true}); - }); + this.library_router.on( 'route:dataset_version', function(folder_id, dataset_id, ldda_id){ + if (Galaxy.libraries.datasetView){ + Galaxy.libraries.datasetView.$el.unbind('click'); + } + Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, ldda_id: ldda_id, show_version: true}); + }); - this.library_router.on('route:folder_permissions', function(folder_id){ - if (Galaxy.libraries.folderView){ - Galaxy.libraries.folderView.$el.unbind('click'); - } - Galaxy.libraries.folderView = new mod_library_folder_view.FolderView({id: folder_id, show_permissions: true}); - }); - this.library_router.on('route:import_datasets', function(folder_id, source){ - if (Galaxy.libraries.folderToolbarView && Galaxy.libraries.folderListView){ - Galaxy.libraries.folderToolbarView.showImportModal({source:source}); - } else { - Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView({id: folder_id}); - Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView({id: folder_id}); - Galaxy.libraries.folderToolbarView.showImportModal({source: source}); - } - }); + this.library_router.on( 'route:dataset_permissions', function(folder_id, dataset_id){ + if (Galaxy.libraries.datasetView){ + Galaxy.libraries.datasetView.$el.unbind('click'); + } + Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, show_permissions: true}); + }); + + this.library_router.on( 'route:library_permissions', function(library_id){ + if (Galaxy.libraries.libraryView){ + Galaxy.libraries.libraryView.$el.unbind('click'); + } + Galaxy.libraries.libraryView = new mod_library_library_view.LibraryView({id: library_id, show_permissions: true}); + }); + + this.library_router.on( 'route:folder_permissions', function(folder_id){ + if (Galaxy.libraries.folderView){ + Galaxy.libraries.folderView.$el.unbind('click'); + } + Galaxy.libraries.folderView = new mod_library_folder_view.FolderView({id: folder_id, show_permissions: true}); + }); + + this.library_router.on( 'route:import_datasets', function( folder_id, source ){ + if ( Galaxy.libraries.folderToolbarView && Galaxy.libraries.folderListView ){ + Galaxy.libraries.folderToolbarView.showImportModal( { source:source } ); + } else { + Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( { id: folder_id } ); + Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: folder_id } ); + Galaxy.libraries.folderToolbarView.showImportModal( { source: source } ); + } + }); Backbone.history.start({pushState: false}); } diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 client/galaxy/scripts/mvc/library/library-folderlist-view.js --- a/client/galaxy/scripts/mvc/library/library-folderlist-view.js +++ b/client/galaxy/scripts/mvc/library/library-folderlist-view.js @@ -15,322 +15,359 @@ ) { var FolderListView = Backbone.View.extend({ - el : '#folder_items_element', - defaults: { - 'include_deleted' : false - }, - // progress percentage - progress: 0, - // progress rate per one item - progressStep: 1, - // self modal - modal : null, + el : '#folder_items_element', + // progress percentage + progress: 0, + // progress rate per one item + progressStep: 1, - folderContainer: null, + folderContainer: null, - sort: 'asc', + sort: 'asc', - events: { - 'click #select-all-checkboxes' : 'selectAll', - 'click .dataset_row' : 'selectClickedRow', - 'click .sort-folder-link' : 'sortColumnClicked' - }, + events: { + 'click #select-all-checkboxes' : 'selectAll', + 'click .dataset_row' : 'selectClickedRow', + 'click .sort-folder-link' : 'sortColumnClicked' + }, - // cache of rendered views - rowViews: {}, - - initialize : function(options){ - this.options = _.defaults(this.options || {}, options); - this.fetchFolder(); - }, + collection: null, - fetchFolder: function(options){ - var options = options || {}; - this.options.include_deleted = options.include_deleted; - var that = this; + defaults: { + include_deleted: false, + page_count: null, + show_page: null + }, - this.collection = new mod_library_model.Folder(); + /** + * Initialize and fetch the folder from the server. + * @param {object} options an object with options + */ + initialize : function( options ){ + this.options = _.defaults( this.options || {}, this.defaults, options ); + this.modal = null; + // map of folder item ids to item views = cache + this.rowViews = {}; - // start to listen if someone modifies collection - this.listenTo(this.collection, 'add', this.renderOne); - this.listenTo(this.collection, 'remove', this.removeOne); + // create a collection of folder items for this view + this.collection = new mod_library_model.Folder(); - this.folderContainer = new mod_library_model.FolderContainer({id: this.options.id}); - this.folderContainer.url = this.folderContainer.attributes.urlRoot + this.options.id + '/contents'; - if (this.options.include_deleted){ - this.folderContainer.url = this.folderContainer.url + '?include_deleted=true'; - } - this.folderContainer.fetch({ - success: function(folder_container) { - that.folder_container = folder_container; - that.render(); - that.addAll(folder_container.get('folder').models); - if (that.options.dataset_id){ - row = _.findWhere(that.rowViews, {id: that.options.dataset_id}); - if (row) { + // start to listen if someone modifies the collection + this.listenTo( this.collection, 'add', this.renderOne ); + this.listenTo( this.collection, 'remove', this.removeOne ); + + this.fetchFolder(); + }, + + fetchFolder: function( options ){ + var options = options || {}; + this.options.include_deleted = options.include_deleted; + var that = this; + + this.folderContainer = new mod_library_model.FolderContainer( { id: this.options.id } ); + this.folderContainer.url = this.folderContainer.attributes.urlRoot + this.options.id + '/contents'; + + if ( this.options.include_deleted ){ + this.folderContainer.url = this.folderContainer.url + '?include_deleted=true'; + } + this.folderContainer.fetch({ + success: function( folder_container ) { + that.folder_container = folder_container; + that.render(); + }, + error: function( model, response ){ + if ( typeof response.responseJSON !== "undefined" ){ + mod_toastr.error( response.responseJSON.err_msg + ' Click this to go back.', '', { onclick: function() { Galaxy.libraries.library_router.back(); } } ); + } else { + mod_toastr.error( 'An error ocurred. Click this to go back.', '', { onclick: function() { Galaxy.libraries.library_router.back(); } } ); + } + } + }); + }, + + render: function ( options ){ + this.options = _.extend( this.options, options ); + var template = this.templateFolder(); + $(".tooltip").hide(); + + // find the upper id in the full path + var path = this.folderContainer.attributes.metadata.full_path; + var upper_folder_id; + if ( path.length === 1 ){ // the library is above us + upper_folder_id = 0; + } else { + upper_folder_id = path[ path.length-2 ][ 0 ]; + } + + this.$el.html( template( { + path: this.folderContainer.attributes.metadata.full_path, + parent_library_id: this.folderContainer.attributes.metadata.parent_library_id, + id: this.options.id, + upper_folder_id: upper_folder_id, + order: this.sort + } ) ); + + // when dataset_id is present render its details too + if ( this.options.dataset_id ){ + row = _.findWhere( that.rowViews, { id: this.options.dataset_id } ); + if ( row ) { row.showDatasetDetails(); } else { - mod_toastr.error('Dataset not found. Showing folder instead.'); + mod_toastr.error( 'Requested dataset not found. Showing folder instead.' ); } + } else { + if ( this.options.show_page === null || this.options.show_page < 1 ){ + this.options.show_page = 1; + } + this.paginate(); + } + $("#center [data-toggle]").tooltip(); + $("#center").css('overflow','auto'); + }, + + paginate: function( options ){ + this.options = _.extend( this.options, options ); + + if ( this.options.show_page === null || this.options.show_page < 1 ){ + this.options.show_page = 1; + } + this.options.total_items_count = this.folder_container.get( 'folder' ).models.length; + this.options.page_count = Math.ceil( this.options.total_items_count / Galaxy.libraries.preferences.get( 'folder_page_size' ) ); + var page_start = ( Galaxy.libraries.preferences.get( 'folder_page_size' ) * ( this.options.show_page - 1 ) ); + var items_to_render = null; + items_to_render = this.folder_container.get( 'folder' ).models.slice( page_start, page_start + Galaxy.libraries.preferences.get( 'folder_page_size' ) ); + this.options.items_shown = items_to_render.length; + // User requests page with no items + if ( Galaxy.libraries.preferences.get( 'folder_page_size' ) * this.options.show_page > ( this.options.total_items_count + Galaxy.libraries.preferences.get( 'folder_page_size' ) ) ){ + items_to_render = []; + } + Galaxy.libraries.folderToolbarView.renderPaginator( this.options ); + this.collection.reset(); + this.addAll( items_to_render ) + }, + + /** + * Adds all given models to the collection. + * @param {array of Item or FolderAsModel} array of models that should + * be added to the view's collection. + */ + addAll: function( models ){ + _.each(models, function( model ) { + Galaxy.libraries.folderListView.collection.add( model ); + }); + $( "#center [data-toggle]" ).tooltip(); + this.checkEmptiness(); + this.postRender(); + }, + + /** + * Call this after all models are added to the collection + * to ensure that the folder toolbar will show proper options + * and that event will be bound on all subviews. + */ + postRender: function(){ + var fetched_metadata = this.folderContainer.attributes.metadata; + fetched_metadata.contains_file = typeof this.collection.findWhere({type: 'file'}) !== 'undefined'; + Galaxy.libraries.folderToolbarView.configureElements(fetched_metadata); + $('.library-row').hover(function() { + $(this).find('.show_on_hover').show(); + }, function () { + $(this).find('.show_on_hover').hide(); + }); + }, + + /** + * Iterates this view's collection and calls the render + * function for each. Also binds the hover behavior. + */ + renderAll: function(){ + var that = this; + _.each( this.collection.models.reverse(), function( model ) { + that.renderOne( model ); + }); + this.postRender(); + }, + + /** + * Creates a view for the given model and adds it to the folder view. + * @param {Item or FolderAsModel} model of the view that will be rendered + */ + renderOne: function(model){ + if (model.get('type') !== 'folder'){ + this.options.contains_file = true; + // model.set('readable_size', this.size_to_string(model.get('file_size'))); } - }, - error: function(model, response){ - if (typeof response.responseJSON !== "undefined"){ - mod_toastr.error(response.responseJSON.err_msg + ' Click this to go back.', '', {onclick: function() {Galaxy.libraries.library_router.back();}}); - } else { - mod_toastr.error('An error ocurred. Click this to go back.', '', {onclick: function() {Galaxy.libraries.library_router.back();}}); - } + model.set('folder_id', this.id); + var rowView = new mod_library_folderrow_view.FolderRowView(model); + + // save new rowView to cache + this.rowViews[model.get('id')] = rowView; + + this.$el.find('#first_folder_item').after(rowView.el); + + $('.library-row').hover(function() { + $(this).find('.show_on_hover').show(); + }, function () { + $(this).find('.show_on_hover').hide(); + }); + }, + + /** + * removes the view of the given model from the DOM + * @param {Item or FolderAsModel} model of the view that will be removed + */ + removeOne: function( model ){ + this.$el.find( '#' + model.id ).remove(); + }, + + /** Checks whether the list is empty and adds/removes the message */ + checkEmptiness : function(){ + if ((this.$el.find('.dataset_row').length === 0) && (this.$el.find('.folder_row').length === 0)){ + this.$el.find('.empty-folder-message').show(); + } else { + this.$el.find('.empty-folder-message').hide(); } - }); - }, + }, - render: function (options) { - this.options = _.defaults(this.options, options); - var template = this.templateFolder(); - $(".tooltip").hide(); + /** User clicked the table heading = he wants to sort stuff */ + sortColumnClicked : function(event){ + event.preventDefault(); + if (this.sort === 'asc'){ + this.sortFolder('name','desc'); + this.sort = 'desc'; + } else { + this.sortFolder('name','asc'); + this.sort = 'asc'; + } + this.render(); + this.renderAll(); + this.checkEmptiness(); + }, - // TODO move to server - // find the upper id in the full path - var path = this.folderContainer.attributes.metadata.full_path; - var upper_folder_id; - if (path.length === 1){ // the library is above us - upper_folder_id = 0; - } else { - upper_folder_id = path[path.length-2][0]; + /** + * Sorts the underlying collection according to the parameters received. + * Currently supports only sorting by name. + */ + sortFolder: function(sort_by, order){ + if (sort_by === 'name'){ + if (order === 'asc'){ + return this.collection.sortByNameAsc(); + } else if (order === 'desc'){ + return this.collection.sortByNameDesc(); + } + } + }, + + /** + * User clicked the checkbox in the table heading + * @param {context} event + */ + selectAll : function (event) { + var selected = event.target.checked; + that = this; + // Iterate each checkbox + $(':checkbox', '#folder_list_body').each(function() { + this.checked = selected; + $row = $(this.parentElement.parentElement); + // Change color of selected/unselected + if (selected) { + that.makeDarkRow($row); + } else { + that.makeWhiteRow($row); + } + }); + }, + + /** + * Check checkbox if user clicks on the whole row or + * on the checkbox itself + */ + selectClickedRow : function (event) { + var checkbox = ''; + var $row; + var source; + if (event.target.localName === 'input'){ + checkbox = event.target; + $row = $(event.target.parentElement.parentElement); + source = 'input'; + } else if (event.target.localName === 'td') { + checkbox = $("#" + event.target.parentElement.id).find(':checkbox')[0]; + $row = $(event.target.parentElement); + source = 'td'; + } + if (checkbox.checked){ + if (source==='td'){ + checkbox.checked = ''; + this.makeWhiteRow($row); + } else if (source==='input') { + this.makeDarkRow($row); + } + } else { + if (source==='td'){ + checkbox.checked = 'selected'; + this.makeDarkRow($row); + } else if (source==='input') { + this.makeWhiteRow($row); + } + } + }, + + makeDarkRow: function($row){ + $row.removeClass('light').addClass('dark'); + $row.find('a').removeClass('light').addClass('dark'); + $row.find('.fa-file-o').removeClass('fa-file-o').addClass('fa-file'); + }, + + makeWhiteRow: function($row){ + $row.removeClass('dark').addClass('light'); + $row.find('a').removeClass('dark').addClass('light'); + $row.find('.fa-file').removeClass('fa-file').addClass('fa-file-o'); + }, + + templateFolder : function (){ + var tmpl_array = []; + + // BREADCRUMBS + tmpl_array.push('<ol class="breadcrumb">'); + tmpl_array.push(' <li><a title="Return to the list of libraries" href="#">Libraries</a></li>'); + tmpl_array.push(' <% _.each(path, function(path_item) { %>'); + tmpl_array.push(' <% if (path_item[0] != id) { %>'); + tmpl_array.push(' <li><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a></li> '); + tmpl_array.push( '<% } else { %>'); + tmpl_array.push(' <li class="active"><span title="You are in this folder"><%- path_item[1] %></span></li>'); + tmpl_array.push(' <% } %>'); + tmpl_array.push(' <% }); %>'); + tmpl_array.push('</ol>'); + + // FOLDER CONTENT + tmpl_array.push('<table data-library-id="<%- parent_library_id %>" id="folder_table" class="grid table table-condensed">'); + tmpl_array.push(' <thead>'); + tmpl_array.push(' <th class="button_heading"></th>'); + tmpl_array.push(' <th style="text-align: center; width: 20px; " title="Check to select all datasets"><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>'); + tmpl_array.push(' <th><a class="sort-folder-link" title="Click to reverse order" href="#">name</a><span title="Sorted alphabetically" class="fa fa-sort-alpha-<%- order %>"></span></th>'); + tmpl_array.push(' <th style="width:5%;">data type</th>'); + tmpl_array.push(' <th style="width:10%;">size</th>'); + tmpl_array.push(' <th style="width:160px;">time updated (UTC)</th>'); + tmpl_array.push(' <th style="width:10%;"></th> '); + tmpl_array.push(' </thead>'); + tmpl_array.push(' <tbody id="folder_list_body">'); + tmpl_array.push(' <tr id="first_folder_item">'); + tmpl_array.push(' <td><a href="#<% if (upper_folder_id !== 0){ print("folders/" + upper_folder_id)} %>" title="Go to parent folder" class="btn_open_folder btn btn-default btn-xs">..<a></td>'); + tmpl_array.push(' <td></td>'); + tmpl_array.push(' <td></td>'); + tmpl_array.push(' <td></td>'); + tmpl_array.push(' <td></td>'); + tmpl_array.push(' <td></td>'); + tmpl_array.push(' <td></td>'); + tmpl_array.push(' </tr>'); + + tmpl_array.push(' </tbody>'); + tmpl_array.push('</table>'); + tmpl_array.push('<div class="empty-folder-message" style="display:none;">This folder is either empty or you do not have proper access permissions to see the contents. If you expected something to show up please consult the <a href="https://wiki.galaxyproject.org/Admin/DataLibraries/LibrarySecurity" target="_blank">library security wikipage</a> or visit the <a href="https://biostar.usegalaxy.org/" target="_blank">Galaxy support site</a>.</div>'); + + return _.template(tmpl_array.join('')); } - - this.$el.html(template({ path: this.folderContainer.attributes.metadata.full_path, parent_library_id: this.folderContainer.attributes.metadata.parent_library_id, id: this.options.id, upper_folder_id: upper_folder_id, order: this.sort})); - - // initialize the library tooltips - $("#center [data-toggle]").tooltip(); - //hack to show scrollbars - $("#center").css('overflow','auto'); - }, - - /** - * Call this after all models are added to the collection - * to ensure that the folder toolbar will show proper options - * and that event will be bound on all subviews. - */ - postRender: function(){ - var fetched_metadata = this.folderContainer.attributes.metadata; - fetched_metadata.contains_file = typeof this.collection.findWhere({type: 'file'}) !== 'undefined'; - Galaxy.libraries.folderToolbarView.configureElements(fetched_metadata); - $('.library-row').hover(function() { - $(this).find('.show_on_hover').show(); - }, function () { - $(this).find('.show_on_hover').hide(); - }); - }, - - /** - * Adds all given models to the collection. - * @param {array of Item or FolderAsModel} array of models that should - * be added to the view's collection. - */ - addAll: function(models){ - _.each(models.reverse(), function(model) { - Galaxy.libraries.folderListView.collection.add(model); - }); - - $("#center [data-toggle]").tooltip(); - this.checkEmptiness(); - - this.postRender(); - }, - - /** - * Iterates this view's collection and calls the render - * function for each. Also binds the hover behavior. - */ - renderAll: function(){ - var that = this; - _.each(this.collection.models.reverse(), function(model) { - that.renderOne(model); - }); - this.postRender(); - }, - - /** - * Creates a view for the given model and adds it to the folder view. - * @param {Item or FolderAsModel} model of the view that will be rendered - */ - renderOne: function(model){ - if (model.get('type') !== 'folder'){ - this.options.contains_file = true; - // model.set('readable_size', this.size_to_string(model.get('file_size'))); - } - model.set('folder_id', this.id); - var rowView = new mod_library_folderrow_view.FolderRowView(model); - - // save new rowView to cache - this.rowViews[model.get('id')] = rowView; - - this.$el.find('#first_folder_item').after(rowView.el); - - $('.library-row').hover(function() { - $(this).find('.show_on_hover').show(); - }, function () { - $(this).find('.show_on_hover').hide(); - }); - }, - - /** - * removes the view of the given model from the DOM - * @param {Item or FolderAsModel} model of the view that will be removed - */ - removeOne: function(model){ - this.$el.find('#' + model.id).remove(); - }, - - /** Checks whether the list is empty and adds/removes the message */ - checkEmptiness : function(){ - if ((this.$el.find('.dataset_row').length === 0) && (this.$el.find('.folder_row').length === 0)){ - this.$el.find('.empty-folder-message').show(); - } else { - this.$el.find('.empty-folder-message').hide(); - } - }, - - /** User clicked the table heading = he wants to sort stuff */ - sortColumnClicked : function(event){ - event.preventDefault(); - if (this.sort === 'asc'){ - this.sortFolder('name','desc'); - this.sort = 'desc'; - } else { - this.sortFolder('name','asc'); - this.sort = 'asc'; - } - this.render(); - this.renderAll(); - this.checkEmptiness(); - }, - - /** - * Sorts the underlying collection according to the parameters received. - * Currently supports only sorting by name. - */ - sortFolder: function(sort_by, order){ - if (sort_by === 'name'){ - if (order === 'asc'){ - return this.collection.sortByNameAsc(); - } else if (order === 'desc'){ - return this.collection.sortByNameDesc(); - } - } - }, - - /** - * User clicked the checkbox in the table heading - * @param {context} event - */ - selectAll : function (event) { - var selected = event.target.checked; - that = this; - // Iterate each checkbox - $(':checkbox', '#folder_list_body').each(function() { - this.checked = selected; - $row = $(this.parentElement.parentElement); - // Change color of selected/unselected - if (selected) { - that.makeDarkRow($row); - } else { - that.makeWhiteRow($row); - } - }); - }, - - /** - * Check checkbox if user clicks on the whole row or - * on the checkbox itself - */ - selectClickedRow : function (event) { - var checkbox = ''; - var $row; - var source; - if (event.target.localName === 'input'){ - checkbox = event.target; - $row = $(event.target.parentElement.parentElement); - source = 'input'; - } else if (event.target.localName === 'td') { - checkbox = $("#" + event.target.parentElement.id).find(':checkbox')[0]; - $row = $(event.target.parentElement); - source = 'td'; - } - if (checkbox.checked){ - if (source==='td'){ - checkbox.checked = ''; - this.makeWhiteRow($row); - } else if (source==='input') { - this.makeDarkRow($row); - } - } else { - if (source==='td'){ - checkbox.checked = 'selected'; - this.makeDarkRow($row); - } else if (source==='input') { - this.makeWhiteRow($row); - } - } - }, - - makeDarkRow: function($row){ - $row.removeClass('light').addClass('dark'); - $row.find('a').removeClass('light').addClass('dark'); - $row.find('.fa-file-o').removeClass('fa-file-o').addClass('fa-file'); - }, - - makeWhiteRow: function($row){ - $row.removeClass('dark').addClass('light'); - $row.find('a').removeClass('dark').addClass('light'); - $row.find('.fa-file').removeClass('fa-file').addClass('fa-file-o'); - }, - - templateFolder : function (){ - var tmpl_array = []; - - // BREADCRUMBS - tmpl_array.push('<ol class="breadcrumb">'); - tmpl_array.push(' <li><a title="Return to the list of libraries" href="#">Libraries</a></li>'); - tmpl_array.push(' <% _.each(path, function(path_item) { %>'); - tmpl_array.push(' <% if (path_item[0] != id) { %>'); - tmpl_array.push(' <li><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a></li> '); - tmpl_array.push( '<% } else { %>'); - tmpl_array.push(' <li class="active"><span title="You are in this folder"><%- path_item[1] %></span></li>'); - tmpl_array.push(' <% } %>'); - tmpl_array.push(' <% }); %>'); - tmpl_array.push('</ol>'); - - // FOLDER CONTENT - tmpl_array.push('<table data-library-id="<%- parent_library_id %>" id="folder_table" class="grid table table-condensed">'); - tmpl_array.push(' <thead>'); - tmpl_array.push(' <th class="button_heading"></th>'); - tmpl_array.push(' <th style="text-align: center; width: 20px; " title="Check to select all datasets"><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>'); - tmpl_array.push(' <th><a class="sort-folder-link" title="Click to reverse order" href="#">name</a><span title="Sorted alphabetically" class="fa fa-sort-alpha-<%- order %>"></span></th>'); - tmpl_array.push(' <th style="width:5%;">data type</th>'); - tmpl_array.push(' <th style="width:10%;">size</th>'); - tmpl_array.push(' <th style="width:160px;">time updated (UTC)</th>'); - tmpl_array.push(' <th style="width:10%;"></th> '); - tmpl_array.push(' </thead>'); - tmpl_array.push(' <tbody id="folder_list_body">'); - tmpl_array.push(' <tr id="first_folder_item">'); - tmpl_array.push(' <td><a href="#<% if (upper_folder_id !== 0){ print("folders/" + upper_folder_id)} %>" title="Go to parent folder" class="btn_open_folder btn btn-default btn-xs">..<a></td>'); - tmpl_array.push(' <td></td>'); - tmpl_array.push(' <td></td>'); - tmpl_array.push(' <td></td>'); - tmpl_array.push(' <td></td>'); - tmpl_array.push(' <td></td>'); - tmpl_array.push(' <td></td>'); - tmpl_array.push(' </tr>'); - - tmpl_array.push(' </tbody>'); - tmpl_array.push('</table>'); - tmpl_array.push('<div class="empty-folder-message" style="display:none;">This folder is either empty or you do not have proper access permissions to see the contents. If you expected something to show up please consult the <a href="https://wiki.galaxyproject.org/Admin/DataLibraries/LibrarySecurity" target="_blank">library security wikipage</a> or visit the <a href="https://biostar.usegalaxy.org/" target="_blank">Galaxy support site</a>.</div>'); - - return _.template(tmpl_array.join('')); - } - + }); return { diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js --- a/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js +++ b/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js @@ -20,7 +20,9 @@ 'click #toolbtn_bulk_import' : 'modalBulkImport', 'click #include_deleted_datasets_chk' : 'checkIncludeDeleted', 'click #toolbtn_show_libinfo' : 'showLibInfo', - 'click #toolbtn_bulk_delete' : 'deleteSelectedDatasets' + 'click #toolbtn_bulk_delete' : 'deleteSelectedDatasets', + 'click #page_size_prompt' : 'showPageSizePrompt' + }, defaults: { @@ -90,6 +92,22 @@ this.$el.html(toolbar_template(template_defaults)); }, + /** + * Called from FolderListView when needed. + * @param {object} options common options + */ + renderPaginator: function( options ){ + this.options = _.extend( this.options, options ); + var paginator_template = this.templatePaginator(); + this.$el.find( '#folder_paginator' ).html( paginator_template({ + id: this.options.id, + show_page: parseInt( this.options.show_page ), + page_count: parseInt( this.options.page_count ), + total_items_count: this.options.total_items_count, + items_shown: this.options.items_shown + })); + }, + configureElements: function(options){ this.options = _.extend(this.options, options); @@ -833,11 +851,11 @@ var popped_item = lddas_set.pop(); if ( typeof popped_item === "undefined" ) { if ( this.options.chain_call_control.failed_number === 0 ){ - mod_toastr.success( 'Selected datasets deleted' ); + mod_toastr.success( 'Selected datasets were deleted.' ); } else if ( this.options.chain_call_control.failed_number === this.options.chain_call_control.total_number ){ - mod_toastr.error( 'There was an error and no datasets were deleted.' ); + mod_toastr.error( 'There was an error and no datasets were deleted. Please make sure you have sufficient permissions.' ); } else if ( this.options.chain_call_control.failed_number < this.options.chain_call_control.total_number ){ - mod_toastr.warning( 'Some of the datasets could not be deleted' ); + mod_toastr.warning( 'Some of the datasets could not be deleted. Please make sure you have sufficient permissions.' ); } Galaxy.modal.hide(); return this.deleted_lddas; @@ -976,6 +994,14 @@ } }, + showPageSizePrompt: function(){ + var folder_page_size = prompt( 'How many items per page do you want to see?', Galaxy.libraries.preferences.get( 'folder_page_size' ) ); + if ( ( folder_page_size != null ) && ( folder_page_size == parseInt( folder_page_size ) ) ) { + Galaxy.libraries.preferences.set( { 'folder_page_size': parseInt( folder_page_size ) } ); + Galaxy.libraries.folderListView.render( { id: this.options.id, show_page: 1 } ); + } + }, + templateToolBar: function(){ tmpl_array = []; @@ -1016,7 +1042,6 @@ tmpl_array.push(' <button style="display:none;" data-toggle="tooltip" data-placement="top" title="Add Datasets to Current Folder" id="toolbtn_add_files" class="btn btn-default toolbtn_add_files primary-button add-library-items" type="button"><span class="fa fa-plus"></span><span class="fa fa-file"></span></span></button>'); tmpl_array.push('<% } %>'); - tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Import selected datasets into history" id="toolbtn_bulk_import" class="primary-button dataset-manipulation" style="margin-left: 0.5em; display:none;" type="button"><span class="fa fa-book"></span> to History</button>'); tmpl_array.push(' <div id="toolbtn_dl" class="btn-group dataset-manipulation" style="margin-left: 0.5em; display:none; ">'); tmpl_array.push(' <button title="Download selected datasets as archive" id="drop_toggle" type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">'); @@ -1032,6 +1057,10 @@ tmpl_array.push(' <button data-id="<%- id %>" data-toggle="tooltip" data-placement="top" title="Show library information" id="toolbtn_show_libinfo" class="primary-button" style="margin-left: 0.5em;" type="button"><span class="fa fa-info-circle"></span> Library Info</button>'); tmpl_array.push(' <span class="help-button" data-toggle="tooltip" data-placement="top" title="Visit Libraries Wiki"><a href="https://wiki.galaxyproject.org/DataLibraries/screen/FolderContents" target="_blank"><button class="primary-button" type="button"><span class="fa fa-question-circle"></span> Help</button></a></span>'); + tmpl_array.push(' <span id="folder_paginator" class="library-paginator">'); + // paginator will append here + tmpl_array.push(' </span>'); + tmpl_array.push(' </div>'); // TOOLBAR END tmpl_array.push(' <div id="folder_items_element">'); @@ -1239,7 +1268,41 @@ tmpl_array.push('</ul>'); return _.template(tmpl_array.join('')); - } + }, + + templatePaginator: function(){ + tmpl_array = []; + + tmpl_array.push(' <ul class="pagination pagination-sm">'); + tmpl_array.push(' <% if ( ( show_page - 1 ) > 0 ) { %>'); + tmpl_array.push(' <% if ( ( show_page - 1 ) > page_count ) { %>'); // we are on higher page than total page count + tmpl_array.push(' <li><a href="#folders/<%= id %>/page/1"><span class="fa fa-angle-double-left"></span></a></li>'); + tmpl_array.push(' <li class="disabled"><a href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>'); + tmpl_array.push(' <% } else { %>'); + tmpl_array.push(' <li><a href="#folders/<%= id %>/page/1"><span class="fa fa-angle-double-left"></span></a></li>'); + tmpl_array.push(' <li><a href="#folders/<%= id %>/page/<% print( show_page - 1 ) %>"><% print( show_page - 1 ) %></a></li>'); + tmpl_array.push(' <% } %>'); + tmpl_array.push(' <% } else { %>'); // we are on the first page + tmpl_array.push(' <li class="disabled"><a href="#folders/<%= id %>/page/1"><span class="fa fa-angle-double-left"></span></a></li>'); + tmpl_array.push(' <li class="disabled"><a href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>'); + tmpl_array.push(' <% } %>'); + tmpl_array.push(' <li class="active">'); + tmpl_array.push(' <a href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page ) %></a>'); + tmpl_array.push(' </li>'); + tmpl_array.push(' <% if ( ( show_page ) < page_count ) { %>'); + tmpl_array.push(' <li><a href="#folders/<%= id %>/page/<% print( show_page + 1 ) %>"><% print( show_page + 1 ) %></a></li>'); + tmpl_array.push(' <li><a href="#folders/<%= id %>/page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>'); + tmpl_array.push(' <% } else { %>'); + tmpl_array.push(' <li class="disabled"><a href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page + 1 ) %></a></li>'); + tmpl_array.push(' <li class="disabled"><a href="#folders/<%= id %>/page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>'); + tmpl_array.push(' <% } %>'); + tmpl_array.push(' </ul>'); + tmpl_array.push(' <span>'); + tmpl_array.push(' showing <a data-toggle="tooltip" data-placement="top" title="Click to change the number of items on page" id="page_size_prompt"><%- items_shown %></a> of <%- total_items_count %> items'); + tmpl_array.push(' </span>'); + + return _.template(tmpl_array.join('')); + }, }); diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 client/galaxy/scripts/mvc/library/library-librarylist-view.js --- a/client/galaxy/scripts/mvc/library/library-librarylist-view.js +++ b/client/galaxy/scripts/mvc/library/library-librarylist-view.js @@ -21,20 +21,19 @@ 'click .sort-libraries-link' : 'sort_clicked' }, - /** - * Initialize and fetch the libraries from server. - * Async render afterwards. - * @param {object} options an options object - */ defaults: { page_count: null, show_page: null }, + /** + * Initialize and fetch the libraries from server. + * Async render afterwards. + * @param {object} options an object with options + */ initialize : function( options ){ this.options = _.defaults( this.options || {}, this.defaults, options ); - - var that = this; + var that = this; this.modal = null; // map of library model ids to library views = cache this.rowViews = {}; @@ -61,19 +60,10 @@ */ render: function ( options ) { this.options = _.extend( this.options, options ); - - if ( ( this.options.page_size != null ) && ( this.options.page_size == parseInt( this.options.page_size ) ) ) { - Galaxy.libraries.preferences.set( { 'library_page_size': parseInt( this.options.page_size ) } ); - } - - $( ".tooltip" ).hide(); - // this.options.show_page = this.options.show_page || 1; var template = this.templateLibraryList(); var libraries_to_render = null; var models = null; - if ( this.options.show_page === null || this.options.show_page < 1 ){ - this.options.show_page = 1; - } + $( ".tooltip" ).hide(); if ( typeof options !== 'undefined' ){ models = typeof options.models !== 'undefined' ? options.models : null; } @@ -89,6 +79,10 @@ } else { libraries_to_render = []; } + // pagination + if ( this.options.show_page === null || this.options.show_page < 1 ){ + this.options.show_page = 1; + } this.options.total_libraries_count = libraries_to_render.length var page_start = ( Galaxy.libraries.preferences.get( 'library_page_size' ) * ( this.options.show_page - 1 ) ); this.options.page_count = Math.ceil( this.options.total_libraries_count / Galaxy.libraries.preferences.get( 'library_page_size' ) ); diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 client/galaxy/scripts/mvc/tools/tools-tree.js --- a/client/galaxy/scripts/mvc/tools/tools-tree.js +++ b/client/galaxy/scripts/mvc/tools/tools-tree.js @@ -126,7 +126,7 @@ // handle default value if (!field.skip) { if (input.optional && field.validate && !field.validate()) { - value = 'None'; + value = null; } add (job_input_id, input.id, value); } diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 lib/galaxy/managers/folders.py --- a/lib/galaxy/managers/folders.py +++ b/lib/galaxy/managers/folders.py @@ -16,14 +16,14 @@ Interface/service object for interacting with folders. """ - def get( self, trans, decoded_folder_id, check_ownership=False, check_accessible=True): + def get( self, trans, decoded_folder_id, check_manageable=False, check_accessible=True): """ Get the folder from the DB. :param decoded_folder_id: decoded folder id :type decoded_folder_id: int - :param check_ownership: flag whether the check that user is owner - :type check_ownership: bool + :param check_manageable: flag whether the check that user can manage item + :type check_manageable: bool :param check_accessible: flag whether to check that user can access item :type check_accessible: bool @@ -38,17 +38,17 @@ raise exceptions.RequestParameterInvalidException( 'No folder found with the id provided.' ) except Exception, e: raise exceptions.InternalServerError( 'Error loading from the database.' + str( e ) ) - folder = self.secure( trans, folder, check_ownership, check_accessible ) + folder = self.secure( trans, folder, check_manageable, check_accessible ) return folder - def secure( self, trans, folder, check_ownership=True, check_accessible=True ): + def secure( self, trans, folder, check_manageable=True, check_accessible=True ): """ - Check if (a) user owns folder or (b) folder is accessible to user. + Check if (a) user can manage folder or (b) folder is accessible to user. :param folder: folder item :type folder: LibraryFolder - :param check_ownership: flag whether the check that user is owner - :type check_ownership: bool + :param check_manageable: flag whether to check that user can manage item + :type check_manageable: bool :param check_accessible: flag whether to check that user can access item :type check_accessible: bool @@ -58,23 +58,26 @@ # all folders are accessible to an admin if trans.user_is_admin(): return folder - if check_ownership: - folder = self.check_ownership( trans, folder ) + if check_manageable: + folder = self.check_manageable( trans, folder ) if check_accessible: folder = self.check_accessible( trans, folder ) return folder - def check_ownership( self, trans, folder ): + def check_manageable( self, trans, folder ): """ - Check whether the user is owner of the folder. + Check whether the user can manage the folder. :returns: the original folder :rtype: LibraryFolder + + :raises: AuthenticationRequired, InsufficientPermissionsException """ if not trans.user: - raise exceptions.AuthenticationRequired( "Must be logged in to manage Galaxy items", type='error' ) - if folder.user != trans.user: - raise exceptions.ItemOwnershipException( "Folder is not owned by the current user", type='error' ) + raise exceptions.AuthenticationRequired( "Must be logged in to manage Galaxy items.", type='error' ) + current_user_roles = trans.get_current_user_roles() + if not trans.app.security_agent.can_manage_library_item( current_user_roles, folder ): + raise exceptions.InsufficientPermissionsException( "You don't have permissions to manage this folder.", type='error' ) else: return folder @@ -135,6 +138,22 @@ trans.app.security_agent.copy_library_permissions( trans, parent_folder, new_folder ) return new_folder + def delete( self, trans, folder, undelete=False ): + """ + Mark given folder deleted/undeleted based on the flag. + + :raises: ItemAccessibilityException + """ + if not trans.user_is_admin(): + folder = self.check_manageable( trans, folder ) + if undelete: + folder.deleted = False + else: + folder.deleted = True + trans.sa_session.add( folder ) + trans.sa_session.flush() + return folder + def get_current_roles( self, trans, folder ): """ Find all roles currently connected to relevant permissions diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2192,7 +2192,7 @@ return roles class LibraryFolder( object, Dictifiable, HasName ): - dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' ) + dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time', 'deleted' ) def __init__( self, name=None, description=None, item_count=0, order_id=None ): self.name = name or "Unnamed folder" self.description = description diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 lib/galaxy/tools/test.py --- a/lib/galaxy/tools/test.py +++ b/lib/galaxy/tools/test.py @@ -196,12 +196,37 @@ if not isinstance(param_value, list): param_value = [ param_value ] processed_value = [ self.__add_uploaded_dataset( context.for_state(), v, param_extra, value ) for v in param_value ] - if isinstance( value, basic.DataCollectionToolParameter ): + elif isinstance( value, basic.DataCollectionToolParameter ): assert 'collection' in param_extra collection_def = param_extra[ 'collection' ] for ( name, value, extra ) in collection_def.collect_inputs(): require_file( name, value, extra, self.required_files ) processed_value = collection_def + elif isinstance( value, basic.SelectToolParameter ): + # Tests may specify values as either raw value or the value + # as they appear in the list - the API doesn't and shouldn't + # accept the text value - so we need to convert the text + # into the form value. + def process_param_value( param_value ): + found_value = False + value_for_text = None + if value.static_options: + for (text, opt_value, selected) in value.static_options: + if param_value == opt_value: + found_value = True + if value_for_text is None and param_value == text: + value_for_text = opt_value + if not found_value and value_for_text is not None: + processed_value = value_for_text + else: + processed_value = param_value + return processed_value + # Do replacement described above for lists or singleton + # values. + if isinstance( param_value, list ): + processed_value = map( process_param_value, param_value ) + else: + processed_value = process_param_value( param_value ) else: processed_value = param_value expanded_inputs[ context.for_state() ] = processed_value diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 lib/galaxy/webapps/galaxy/api/folders.py --- a/lib/galaxy/webapps/galaxy/api/folders.py +++ b/lib/galaxy/webapps/galaxy/api/folders.py @@ -44,7 +44,7 @@ :rtype: dict """ folder_id = self.folder_manager.cut_and_decode( trans, id ) - folder = self.folder_manager.get( trans, folder_id, check_ownership=False, check_accessible=True ) + folder = self.folder_manager.get( trans, folder_id, check_manageable=False, check_accessible=True ) return_dict = self.folder_manager.get_folder_dict( trans, folder ) return return_dict @@ -229,6 +229,32 @@ 'Allowed values are: "set_permissions"' ) return self.folder_manager.get_current_roles( trans, folder ) + @expose_api + def delete( self, trans, id, **kwd ): + """ + delete( self, trans, id, **kwd ) + * DELETE /api/folders/{id} + marks the folder with the given ``id`` as `deleted` (or removes the `deleted` mark if the `undelete` param is true) + + .. note:: Currently, only admin users can un/delete folders. + + :param id: the encoded id of the folder to un/delete + :type id: an encoded id string + + :param undelete: (optional) flag specifying whether the item should be deleted or undeleted, defaults to false: + :type undelete: bool + + :returns: detailed folder information + :rtype: dictionary + + :raises: ItemAccessibilityException, MalformedId, ObjectNotFound + """ + folder = self.folder_manager.get( trans, self.folder_manager.cut_and_decode( trans, id ), True ) + undelete = util.string_as_bool( kwd.get( 'undelete', False ) ) + folder = self.folder_manager.delete( trans, folder, undelete ) + folder_dict = self.folder_manager.get_folder_dict( trans, folder ) + return folder_dict + @web.expose_api def update( self, trans, id, library_id, payload, **kwd ): """ diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 lib/galaxy/webapps/galaxy/controllers/dataset.py --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -932,11 +932,14 @@ @web.expose def show_params( self, trans, dataset_id=None, from_noframe=None, **kwd ): """ - Show the parameters used for an HDA + Show the parameters used for the job associated with an HDA """ - hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) ) + try: + hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) ) + except ValueError: + hda = None if not hda: - raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) ) + raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % escape( str( dataset_id ) ) ) if not self._can_access_dataset( trans, hda ): return trans.show_error_message( "You are not allowed to access this dataset" ) @@ -961,19 +964,30 @@ toolbox = self.get_toolbox() tool = toolbox.get_tool( job.tool_id ) assert tool is not None, 'Requested tool has not been loaded.' - #Load parameter objects, if a parameter type has changed, it's possible for the value to no longer be valid + # Load parameter objects, if a parameter type has changed, it's possible for the value to no longer be valid try: params_objects = job.get_param_values( trans.app, ignore_errors=False ) except: params_objects = job.get_param_values( trans.app, ignore_errors=True ) - upgrade_messages = tool.check_and_update_param_values( job.get_param_values( trans.app, ignore_errors=True ), trans, update_values=False ) #use different param_objects here, since we want to display original values as much as possible + # use different param_objects in the following line, since we want to display original values as much as possible + upgrade_messages = tool.check_and_update_param_values( job.get_param_values( trans.app, ignore_errors=True ), + trans, + update_values=False ) has_parameter_errors = True except: pass if job is None: return trans.show_error_message( "Job information is not available for this dataset." ) - #TODO: we should provide the basic values along with the objects, in order to better handle reporting of old values during upgrade - return trans.fill_template( "show_params.mako", inherit_chain=inherit_chain, history=trans.get_history(), hda=hda, job=job, tool=tool, params_objects=params_objects, upgrade_messages=upgrade_messages, has_parameter_errors=has_parameter_errors ) + # TODO: we should provide the basic values along with the objects, in order to better handle reporting of old values during upgrade + return trans.fill_template( "show_params.mako", + inherit_chain=inherit_chain, + history=trans.get_history(), + hda=hda, + job=job, + tool=tool, + params_objects=params_objects, + upgrade_messages=upgrade_messages, + has_parameter_errors=has_parameter_errors ) @web.expose def copy_datasets( self, trans, source_history=None, source_content_ids="", target_history_id=None, target_history_ids="", new_history_name="", do_copy=False, **kwd ): diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 lib/galaxy/webapps/galaxy/controllers/search.py --- a/lib/galaxy/webapps/galaxy/controllers/search.py +++ b/lib/galaxy/webapps/galaxy/controllers/search.py @@ -1,4 +1,3 @@ - """ Contains a basic search interface for Galaxy """ @@ -8,7 +7,13 @@ log = logging.getLogger( __name__ ) + class SearchController( BaseUIController ): + @web.expose def index(self, trans): - return trans.fill_template( "search/index.mako") + """ + Per the message, this is not ready for human consumption, yet. Power + users can still use the search API. + """ + return trans.show_message("Sorry, the search interface isn't quite ready for use, yet. Watch the release notes and check back later!") diff -r 46a3d43d779fa7735029449935dad77474adf9b1 -r 719191cd0f3ea8ad2ff02ec303dabf0e22c31e60 static/scripts/galaxy.library.js --- a/static/scripts/galaxy.library.js +++ b/static/scripts/galaxy.library.js @@ -36,7 +36,7 @@ initialize: function() { this.routesHit = 0; //keep count of number of routes handled by the application - Backbone.history.on('route', function() { this.routesHit++; }, this); + Backbone.history.on( 'route', function() { this.routesHit++; }, this ); }, routes: { @@ -45,6 +45,7 @@ "library/:library_id/permissions" : "library_permissions", "folders/:folder_id/permissions" : "folder_permissions", "folders/:id" : "folder_content", + "folders/:id/page/:show_page" : "folder_page", "folders/:folder_id/datasets/:dataset_id" : "dataset_detail", "folders/:folder_id/datasets/:dataset_id/permissions" : "dataset_permissions", "folders/:folder_id/datasets/:dataset_id/versions/:ldda_id" : "dataset_version", @@ -53,13 +54,13 @@ }, back: function() { - if(this.routesHit > 1) { + if( this.routesHit > 1 ) { //more than one route hit -> user did not land to current page directly window.history.back(); } else { //otherwise go to the home page. Use replaceState if available so //the navigation doesn't create an extra history entry - this.navigate('#', {trigger:true, replace:true}); + this.navigate( '#', { trigger:true, replace:true } ); } } }); @@ -71,7 +72,8 @@ with_deleted : false, sort_order : 'asc', sort_by : 'name', - library_page_size : 20 + library_page_size : 20, + folder_page_size : 15 } }); @@ -94,10 +96,10 @@ this.library_router = new LibraryRouter(); - this.library_router.on('route:libraries', function() { - Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView(); - Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView(); - }); + this.library_router.on( 'route:libraries', function() { + Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView(); + Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView(); + }); this.library_router.on('route:libraries_page', function( show_page ) { if ( Galaxy.libraries.libraryToolbarView === null ){ @@ -108,66 +110,77 @@ } }); - this.library_router.on('route:folder_content', function(id) { + this.library_router.on( 'route:folder_content', function( id ) { if (Galaxy.libraries.folderToolbarView){ - Galaxy.libraries.folderToolbarView.$el.unbind('click'); - } - Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView({id: id}); - Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView({id: id}); - }); + Galaxy.libraries.folderToolbarView.$el.unbind( 'click' ); + } + Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( { id: id } ); + Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: id } ); + }); - this.library_router.on('route:download', function(folder_id, format) { - if ($('#folder_list_body').find(':checked').length === 0) { - mod_toastr.info( 'You must select at least one dataset to download' ); - Galaxy.libraries.library_router.navigate('folders/' + folder_id, {trigger: true, replace: true}); - } else { - Galaxy.libraries.folderToolbarView.download(folder_id, format); - Galaxy.libraries.library_router.navigate('folders/' + folder_id, {trigger: false, replace: true}); - } - }); + this.library_router.on( 'route:folder_page', function( id, show_page ) { + if ( Galaxy.libraries.folderToolbarView === null ){ + Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( {id: id} ); + Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: id, show_page: show_page } ); + } else { + Galaxy.libraries.folderListView.render( { id: id, show_page: parseInt( show_page ) } ) + } + }); - this.library_router.on('route:dataset_detail', function(folder_id, dataset_id){ - if (Galaxy.libraries.datasetView){ - Galaxy.libraries.datasetView.$el.unbind('click'); - } - Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id}); - }); - this.library_router.on('route:dataset_version', function(folder_id, dataset_id, ldda_id){ - if (Galaxy.libraries.datasetView){ - Galaxy.libraries.datasetView.$el.unbind('click'); - } - Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, ldda_id: ldda_id, show_version: true}); - }); + this.library_router.on( 'route:download', function( folder_id, format ) { + if ( $( '#folder_list_body' ).find( ':checked' ).length === 0 ) { + mod_toastr.info( 'You must select at least one dataset to download' ); + Galaxy.libraries.library_router.navigate( 'folders/' + folder_id, { trigger: true, replace: true } ); + } else { + Galaxy.libraries.folderToolbarView.download( folder_id, format ); + Galaxy.libraries.library_router.navigate( 'folders/' + folder_id, { trigger: false, replace: true } ); + } + }); - this.library_router.on('route:dataset_permissions', function(folder_id, dataset_id){ - if (Galaxy.libraries.datasetView){ - Galaxy.libraries.datasetView.$el.unbind('click'); - } - Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, show_permissions: true}); - }); + this.library_router.on( 'route:dataset_detail', function(folder_id, dataset_id){ + if (Galaxy.libraries.datasetView){ + Galaxy.libraries.datasetView.$el.unbind('click'); + } + Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id}); + }); - this.library_router.on('route:library_permissions', function(library_id){ - if (Galaxy.libraries.libraryView){ - Galaxy.libraries.libraryView.$el.unbind('click'); - } - Galaxy.libraries.libraryView = new mod_library_library_view.LibraryView({id: library_id, show_permissions: true}); - }); + this.library_router.on( 'route:dataset_version', function(folder_id, dataset_id, ldda_id){ + if (Galaxy.libraries.datasetView){ + Galaxy.libraries.datasetView.$el.unbind('click'); + } + Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, ldda_id: ldda_id, show_version: true}); + }); - this.library_router.on('route:folder_permissions', function(folder_id){ - if (Galaxy.libraries.folderView){ - Galaxy.libraries.folderView.$el.unbind('click'); - } - Galaxy.libraries.folderView = new mod_library_folder_view.FolderView({id: folder_id, show_permissions: true}); - }); - this.library_router.on('route:import_datasets', function(folder_id, source){ - if (Galaxy.libraries.folderToolbarView && Galaxy.libraries.folderListView){ - Galaxy.libraries.folderToolbarView.showImportModal({source:source}); - } else { - Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView({id: folder_id}); - Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView({id: folder_id}); - Galaxy.libraries.folderToolbarView.showImportModal({source: source}); - } - }); + this.library_router.on( 'route:dataset_permissions', function(folder_id, dataset_id){ + if (Galaxy.libraries.datasetView){ + Galaxy.libraries.datasetView.$el.unbind('click'); + } + Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, show_permissions: true}); + }); + + this.library_router.on( 'route:library_permissions', function(library_id){ + if (Galaxy.libraries.libraryView){ + Galaxy.libraries.libraryView.$el.unbind('click'); + } + Galaxy.libraries.libraryView = new mod_library_library_view.LibraryView({id: library_id, show_permissions: true}); + }); + + this.library_router.on( 'route:folder_permissions', function(folder_id){ + if (Galaxy.libraries.folderView){ + Galaxy.libraries.folderView.$el.unbind('click'); + } + Galaxy.libraries.folderView = new mod_library_folder_view.FolderView({id: folder_id, show_permissions: true}); + }); + + this.library_router.on( 'route:import_datasets', function( folder_id, source ){ + if ( Galaxy.libraries.folderToolbarView && Galaxy.libraries.folderListView ){ + Galaxy.libraries.folderToolbarView.showImportModal( { source:source } ); + } else { + Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( { id: folder_id } ); + Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: folder_id } ); + Galaxy.libraries.folderToolbarView.showImportModal( { source: source } ); + } + }); Backbone.history.start({pushState: false}); } This diff is so big that we needed to truncate the remainder. 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.