diff --git a/.ci/flake8_lint_include_list.txt b/.ci/flake8_lint_include_list.txt index 4789b884112c..4cb3e453782f 100644 --- a/.ci/flake8_lint_include_list.txt +++ b/.ci/flake8_lint_include_list.txt @@ -101,7 +101,7 @@ lib/galaxy/webapps/galaxy/api/genomes.py lib/galaxy/webapps/galaxy/api/histories.py lib/galaxy/webapps/galaxy/api/__init__.py lib/galaxy/webapps/galaxy/api/jobs.py -lib/galaxy/webapps/galaxy/api/lda_datasets.py +lib/galaxy/webapps/galaxy/api/library_datasets.py lib/galaxy/webapps/galaxy/api/requests.py lib/galaxy/webapps/galaxy/api/roles.py lib/galaxy/webapps/galaxy/api/samples.py diff --git a/.ci/jenkins/selenium/docker-compose.yml b/.ci/jenkins/selenium/docker-compose.yml index 6f23b3081dee..566bc6565ec2 100644 --- a/.ci/jenkins/selenium/docker-compose.yml +++ b/.ci/jenkins/selenium/docker-compose.yml @@ -12,7 +12,7 @@ services: ports: - "${GALAXY_PORT}:8080" selenium: - image: selenium/standalone-chrome:3.0.1-aluminum + image: selenium/standalone-chrome:3.5.2 ports: - "${SELENIUM_PORT}:4444" links: diff --git a/client/galaxy/scripts/apps/admin.js b/client/galaxy/scripts/apps/admin.js index 33bdc1206334..64a2845862ec 100644 --- a/client/galaxy/scripts/apps/admin.js +++ b/client/galaxy/scripts/apps/admin.js @@ -7,6 +7,7 @@ var jQuery = require( 'jquery' ), Ui = require( 'mvc/ui/ui-misc' ), QueryStringParsing = require( 'utils/query-string-parsing' ), Router = require( 'layout/router' ), + Utils = require( 'utils/utils' ), Page = require( 'layout/page' ); window.app = function app( options, bootstrapped ){ @@ -127,9 +128,10 @@ window.app = function app( options, bootstrapped ){ $(function() { _.extend( options.config, { active_view : 'admin' } ); + Utils.setWindowTitle("Administration"); Galaxy.page = new Page.View( _.extend( options, { Left : AdminPanel, Router : AdminRouter } ) ); }); -}; \ No newline at end of file +}; diff --git a/client/galaxy/scripts/layout/page.js b/client/galaxy/scripts/layout/page.js index 57bb81a1f2c5..1a107d6ae79b 100644 --- a/client/galaxy/scripts/layout/page.js +++ b/client/galaxy/scripts/layout/page.js @@ -1,4 +1,4 @@ -define( [ 'layout/masthead', 'layout/panel', 'mvc/ui/ui-modal' ], function( Masthead, Panel, Modal ) { +define( [ 'layout/masthead', 'layout/panel', 'mvc/ui/ui-modal', 'utils/utils' ], function( Masthead, Panel, Modal, Utils) { var View = Backbone.View.extend({ el : 'body', className : 'full-content', @@ -16,7 +16,16 @@ define( [ 'layout/masthead', 'layout/panel', 'mvc/ui/ui-modal' ], function( Mast // attach global objects, build mastheads Galaxy.modal = this.modal = new Modal.View(); - Galaxy.display = this.display = function( view ) { self.center.display( view ) }; + Galaxy.display = this.display = function( view ) { + if ( view.title ){ + Utils.setWindowTitle( view.title ); + view.allow_title_display = false; + } else { + Utils.setWindowTitle(); + view.allow_title_display = true; + } + self.center.display( view ); + }; Galaxy.router = this.router = options.Router && new options.Router( self, options ); this.masthead = new Masthead.View( this.config ); this.center = new Panel.CenterPanel(); @@ -155,7 +164,7 @@ define( [ 'layout/masthead', 'layout/panel', 'mvc/ui/ui-modal' ], function( Mast } } }) - .error( function( data ) { + .error( function( data ) { // hide the communication icon if the communication server is not available $chat_icon_element.css( "visibility", "hidden" ); }); @@ -166,4 +175,4 @@ define( [ 'layout/masthead', 'layout/panel', 'mvc/ui/ui-modal' ], function( Mast }); return { View: View } -}); \ No newline at end of file +}); diff --git a/client/galaxy/scripts/mvc/collection/list-of-pairs-collection-creator.js b/client/galaxy/scripts/mvc/collection/list-of-pairs-collection-creator.js index 884f9a51180d..c19c6ae0cfa6 100644 --- a/client/galaxy/scripts/mvc/collection/list-of-pairs-collection-creator.js +++ b/client/galaxy/scripts/mvc/collection/list-of-pairs-collection-creator.js @@ -405,7 +405,7 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte /** autopair by exact match */ autopairSimple : autoPairFnBuilder({ - scoreThreshold: function(){ return 1.0; }, + scoreThreshold: function(){ return 0.6; }, match : function _match( params ){ params = params || {}; if( params.matchTo === params.possible ){ diff --git a/client/galaxy/scripts/mvc/grid/grid-shared.js b/client/galaxy/scripts/mvc/grid/grid-shared.js index 11a0fecb89a5..141e7c9123e2 100644 --- a/client/galaxy/scripts/mvc/grid/grid-shared.js +++ b/client/galaxy/scripts/mvc/grid/grid-shared.js @@ -6,6 +6,7 @@ define( [ 'mvc/grid/grid-view' ], function( GridView ) { this.setElement( $( '
' ) ); this.model = new Backbone.Model( options ); this.item = this.model.get( 'item' ); + this.title = this.model.get('plural'); $.ajax({ url : Galaxy.root + this.item + '/' + this.model.get( 'action_id' ), success : function( response ) { @@ -55,4 +56,4 @@ define( [ 'mvc/grid/grid-view' ], function( GridView ) { return { View: View } -}); \ No newline at end of file +}); diff --git a/client/galaxy/scripts/mvc/grid/grid-view.js b/client/galaxy/scripts/mvc/grid/grid-view.js index b5176d4d0716..4877c91f9e17 100644 --- a/client/galaxy/scripts/mvc/grid/grid-view.js +++ b/client/galaxy/scripts/mvc/grid/grid-view.js @@ -20,6 +20,7 @@ return Backbone.View.extend({ initialize: function(grid_config) { this.grid = new GridModel(); this.dict_format = grid_config.dict_format; + this.title = grid_config.title; var self = this; window.add_tag_to_grid_filter = function( tag_name, tag_value ){ // Put tag name and value together. @@ -83,6 +84,9 @@ return Backbone.View.extend({ // get options var options = this.grid.attributes; + if (this.allow_title_display && options.title){ + Utils.setWindowTitle(options.title); + } // handle refresh requests this.handle_refresh(options.refresh_frames); diff --git a/client/galaxy/scripts/mvc/history/history-list.js b/client/galaxy/scripts/mvc/history/history-list.js index 05f12680ec91..7c4021a61589 100644 --- a/client/galaxy/scripts/mvc/history/history-list.js +++ b/client/galaxy/scripts/mvc/history/history-list.js @@ -2,6 +2,7 @@ define( [ 'utils/utils', 'mvc/grid/grid-view', 'mvc/history/history-model', 'mvc/history/copy-dialog' ], function( Utils, GridView, HistoryModel, historyCopyDialog ) { var View = Backbone.View.extend({ + title: "Histories", initialize: function( options ) { var self = this; this.setElement( $( '
' ) ); @@ -46,4 +47,4 @@ define( [ 'utils/utils', 'mvc/grid/grid-view', 'mvc/history/history-model', 'mvc return { View: View } -}); \ No newline at end of file +}); diff --git a/client/galaxy/scripts/mvc/history/history-view-edit.js b/client/galaxy/scripts/mvc/history/history-view-edit.js index 7fb67717a69b..33bbc51c64f1 100644 --- a/client/galaxy/scripts/mvc/history/history-view-edit.js +++ b/client/galaxy/scripts/mvc/history/history-view-edit.js @@ -156,6 +156,7 @@ var HistoryViewEdit = _super.extend( renderItems : function( $whereTo ){ var views = _super.prototype.renderItems.call( this, $whereTo ); if( !this.searchFor ){ this._renderCounts( $whereTo ); } + else{ this._renderSearchFindings( $whereTo ); } return views; }, @@ -427,10 +428,10 @@ var HistoryViewEdit = _super.extend( }, /** override to display number found in subtitle */ - _renderSearchFindings : function(){ - this.$( '> .controls .subtitle' ).html([ - _l( 'Found' ), this.views.length - ].join(' ')); + _renderSearchFindings : function( $whereTo ){ + $whereTo = $whereTo instanceof jQuery? $whereTo : this.$el; + var html = this.templates.found( this.model.toJSON(), this ); + $whereTo.find( '> .controls .subtitle' ).html( html ); return this; }, @@ -598,8 +599,37 @@ HistoryViewEdit.prototype.templates = (function(){ '<% } %>', ], 'history' ); + var foundTemplate = BASE_MVC.wrapTemplate([ + _l( 'Found' ), ' <%- view.views.length %>, ', + + '<% if( history.contents_active.deleted ){ %>', + '<% if( view.model.contents.includeDeleted ){ %>', + '', + _l( 'hide deleted' ), + ', ', + '<% } else { %>', + '', + _l( 'show deleted' ), + ', ', + '<% } %>', + '<% } %>', + + '<% if( history.contents_active.hidden ){ %>', + '<% if( view.model.contents.includeHidden ){ %>', + '', + _l( 'hide hidden' ), + '', + '<% } else { %>', + '', + _l( 'show hidden' ), + '', + '<% } %>', + '<% } %>', + ], 'history' ); + return _.extend( _.clone( _super.prototype.templates ), { - counts : countsTemplate + counts : countsTemplate, + found : foundTemplate }); }()); diff --git a/client/galaxy/scripts/mvc/history/options-menu.js b/client/galaxy/scripts/mvc/history/options-menu.js index 3746e3857e47..d8c2b26809b1 100644 --- a/client/galaxy/scripts/mvc/history/options-menu.js +++ b/client/galaxy/scripts/mvc/history/options-menu.js @@ -65,7 +65,7 @@ var menu = [ anon : true, func : function() { if( Galaxy && Galaxy.currHistoryPanel && confirm( _l( 'Really delete the current history?' ) ) ){ - galaxy_main.window.location.href = 'history/delete?id=' + Galaxy.currHistoryPanel.model.id; + Galaxy.currHistoryPanel.model._delete().done(function(){Galaxy.currHistoryPanel.loadCurrentHistory();}); } }, }, @@ -76,7 +76,7 @@ var menu = [ func : function() { if( Galaxy && Galaxy.currHistoryPanel && confirm( _l( 'Really delete the current history permanently? This cannot be undone.' ) ) ){ - galaxy_main.window.location.href = 'history/delete?purge=True&id=' + Galaxy.currHistoryPanel.model.id; + Galaxy.currHistoryPanel.model.purge().done(function(){Galaxy.currHistoryPanel.loadCurrentHistory();}); } }, }, diff --git a/client/galaxy/scripts/mvc/library/library-dataset-view.js b/client/galaxy/scripts/mvc/library/library-dataset-view.js index ddc4f5564931..1a5613737206 100644 --- a/client/galaxy/scripts/mvc/library/library-dataset-view.js +++ b/client/galaxy/scripts/mvc/library/library-dataset-view.js @@ -16,8 +16,10 @@ var LibraryDatasetView = Backbone.View.extend({ model: null, - options: { + options: {}, + defaults: { + edit_mode: false }, events: { @@ -25,13 +27,11 @@ var LibraryDatasetView = Backbone.View.extend({ "click .toolbtn_cancel_modifications" : "render", "click .toolbtn-download-dataset" : "downloadDataset", "click .toolbtn-import-dataset" : "importIntoHistory", - "click .toolbtn-share-dataset" : "shareDataset", "click .btn-copy-link-to-clipboard" : "copyToClipboard", "click .btn-make-private" : "makeDatasetPrivate", "click .btn-remove-restrictions" : "removeDatasetRestrictions", "click .toolbtn_save_permissions" : "savePermissions", - "click .toolbtn_save_modifications" : "comingSoon", - + "click .toolbtn_save_modifications" : "saveModifications" }, // genome select @@ -134,7 +134,10 @@ var LibraryDatasetView = Backbone.View.extend({ $(".tooltip").remove(); var template = this.templateModifyDataset(); this.$el.html(template({item: this.model})); - this.renderSelectBoxes({genome_build: this.model.get('genome_build'), file_ext: this.model.get('file_ext') }); + this.renderSelectBoxes({ + genome_build: this.model.get('genome_build'), + file_ext: this.model.get('file_ext') + }); $(".peek").html(this.model.get("peek")); $("#center [data-toggle]").tooltip(); }, @@ -245,10 +248,6 @@ var LibraryDatasetView = Backbone.View.extend({ }); }, - shareDataset: function(){ - mod_toastr.info('Feature coming soon.'); - }, - goBack: function(){ Galaxy.libraries.library_router.back(); }, @@ -457,8 +456,57 @@ var LibraryDatasetView = Backbone.View.extend({ } }, - comingSoon: function(){ - mod_toastr.warning('Feature coming soon.'); + /** + * Save the changes made to the library dataset. + */ + saveModifications: function(options){ + var is_changed = false; + var ld = this.model; + var new_name = this.$el.find('.input_dataset_name').val(); + if (typeof new_name !== 'undefined' && new_name !== ld.get('name') ){ + if (new_name.length > 0){ + ld.set("name", new_name); + is_changed = true; + } else{ + mod_toastr.warning('Library dataset name has to be at least 1 character long.'); + return; + } + } + var new_info = this.$el.find('.input_dataset_misc_info').val(); + if (typeof new_info !== 'undefined' && new_info !== ld.get('misc_info') ){ + ld.set("misc_info", new_info); + is_changed = true; + } + var new_genome_build = this.select_genome.$el.select2('data').id; + if (typeof new_genome_build !== 'undefined' && new_genome_build !== ld.get('genome_build') ){ + ld.set("genome_build", new_genome_build); + is_changed = true; + } + var new_ext = this.select_extension.$el.select2('data').id; + if (typeof new_ext !== 'undefined' && new_ext !== ld.get('file_ext') ){ + ld.set("file_ext", new_ext); + is_changed = true; + } + var dataset_view = this; + if (is_changed){ + ld.save(null, { + patch: true, + success: function(ld) { + dataset_view.render() + mod_toastr.success('Changes to library dataset saved.'); + }, + error: function(model, response){ + if (typeof response.responseJSON !== "undefined"){ + mod_toastr.error(response.responseJSON.err_msg); + } else { + mod_toastr.error('An error occured while attempting to update the library dataset.'); + } + } + }); + } else { + dataset_view.render() + mod_toastr.info('Nothing has changed.'); + } }, copyToClipboard: function(){ @@ -523,42 +571,46 @@ var LibraryDatasetView = Backbone.View.extend({ }, /** - * Request all extensions and genomes from Galaxy - * and save them sorted in arrays. + * If needed request all extensions and/or genomes from Galaxy + * and save them in sorted arrays. */ fetchExtAndGenomes: function(){ var that = this; - mod_utils.get({ - url : Galaxy.root + "api/datatypes?extension_only=False", - success : function( datatypes ) { - for (var key in datatypes) { - that.list_extensions.push({ - id : datatypes[key].extension, - text : datatypes[key].extension, - description : datatypes[key].description, - description_url : datatypes[key].description_url - }); - } - that.list_extensions.sort(function(a, b) { - return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; - }); - that.list_extensions.unshift(that.auto); - } + if (this.list_genomes.length == 0){ + mod_utils.get({ + url : Galaxy.root + "api/datatypes?extension_only=False", + success : function( datatypes ) { + for (var key in datatypes) { + that.list_extensions.push({ + id : datatypes[key].extension, + text : datatypes[key].extension, + description : datatypes[key].description, + description_url : datatypes[key].description_url + }); + } + that.list_extensions.sort(function(a, b) { + return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; + }); + that.list_extensions.unshift(that.auto); + } }); - mod_utils.get({ - url : Galaxy.root + "api/genomes", + } + if (this.list_extensions.length == 0){ + mod_utils.get({ + url : Galaxy.root + "api/genomes", success : function( genomes ) { - for (var key in genomes ) { - that.list_genomes.push({ - id : genomes[key][1], - text : genomes[key][0] - }); - } - that.list_genomes.sort(function(a, b) { - return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; - }); - } - }); + for (var key in genomes ) { + that.list_genomes.push({ + id : genomes[key][1], + text : genomes[key][0] + }); + } + that.list_genomes.sort(function(a, b) { + return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; + }); + } + }); + } }, renderSelectBoxes: function(options){ @@ -566,6 +618,7 @@ var LibraryDatasetView = Backbone.View.extend({ // See this.fetchExtAndGenomes() // TODO switch to common resources: // https://trello.com/c/dIUE9YPl/1933-ui-common-resources-and-data-into-galaxy-object + var that = this; var current_genome = '?'; var current_ext = 'auto'; if (typeof options !== 'undefined'){ @@ -576,17 +629,16 @@ var LibraryDatasetView = Backbone.View.extend({ current_ext = options.file_ext; } } - var that = this; this.select_genome = new mod_select.View( { css: 'dataset-genome-select', data: that.list_genomes, - container: that.$el.find( '#dataset_genome_select' ), + container: that.$el.find('#dataset_genome_select'), value: current_genome } ); this.select_extension = new mod_select.View({ css: 'dataset-extension-select', data: that.list_extensions, - container: that.$el.find( '#dataset_extension_select' ), + container: that.$el.find('#dataset_extension_select'), value: current_ext }); }, @@ -711,13 +763,13 @@ var LibraryDatasetView = Backbone.View.extend({ '<% } %>', '<% if (item.get("misc_blurb")) { %>', '', - 'Miscellaneous blurb', + 'Misc. blurb', '<%= _.escape(item.get("misc_blurb")) %>', '', '<% } %>', '<% if (item.get("misc_info")) { %>', '', - 'Miscellaneous information', + 'Misc. info', '<%= _.escape(item.get("misc_info")) %>', '', '<% } %>', @@ -902,7 +954,6 @@ var LibraryDatasetView = Backbone.View.extend({ '', '
', - '

For full editing options please import the dataset to history and use "Edit attributes" on it.

', '', '', '', @@ -956,12 +1007,12 @@ var LibraryDatasetView = Backbone.View.extend({ '', '', '', - '', - '', + '', + '', '', '', - '', - '', + '', + '', '', //TODO: add functionality to modify tags here '<% if (item.get("tags")) { %>', diff --git a/client/galaxy/scripts/mvc/library/library-folder-view.js b/client/galaxy/scripts/mvc/library/library-folder-view.js index a66019ccbd78..193ba299ec63 100644 --- a/client/galaxy/scripts/mvc/library/library-folder-view.js +++ b/client/galaxy/scripts/mvc/library/library-folder-view.js @@ -37,8 +37,6 @@ var FolderView = Backbone.View.extend({ success: function() { if (that.options.show_permissions){ that.showPermissions(); - } else { - that.render(); } }, error: function(model, response){ @@ -51,19 +49,6 @@ var FolderView = Backbone.View.extend({ }); }, - render: function(options){ - $(".tooltip").remove(); - this.options = _.extend(this.options, options); - var template = this.templateFolder(); - this.$el.html(template({item: this.model})); - $(".peek").html(this.model.get("peek")); - $("#center [data-toggle]").tooltip(); - }, - - shareFolder: function(){ - mod_toastr.info('Feature coming soon.'); - }, - goBack: function(){ Galaxy.libraries.library_router.back(); }, @@ -165,18 +150,6 @@ var FolderView = Backbone.View.extend({ return select_options; }, - comingSoon: function(){ - mod_toastr.warning('Feature coming soon.'); - }, - - copyToClipboard: function(){ - var href = Backbone.history.location.href; - if (href.lastIndexOf('/permissions') !== -1){ - href = href.substr(0, href.lastIndexOf('/permissions')); - } - window.prompt("Copy to clipboard: Ctrl+C, Enter", href); - }, - /** * Extract the role ids from Select2 elements's 'data' */ @@ -206,57 +179,6 @@ var FolderView = Backbone.View.extend({ }) }, - templateFolder : function(){ - return _.template([ - '
', - '
', - '', - '/datasets/<%- item.id %>/permissions">', - '', - '', - '', - '
', - '

', - 'This dataset is unrestricted so everybody can access it. Just share the URL of this page. ', - ' ', - '

', - '
', - '
">Name<%= _.escape(item.get("message")) %>
Miscellaneous information<%= _.escape(item.get("misc_info")) %>Misc. blurb<%= _.escape(item.get("misc_blurb")) %>
Miscellaneous blurb<%= _.escape(item.get("misc_blurb")) %>Misc. information">
', - '', - '', - '', - '', - '<% if (item.get("file_ext")) { %>', - '', - '', - '', - '', - '<% } %>', - '
">', - 'Name', - '', - '<%= _.escape(item.get("name")) %>', - '
Data type', - '<%= _.escape(item.get("file_ext")) %>', - '
', - '
', - '
' - ].join('')); - }, - templateFolderPermissions : function(){ return _.template([ '
', diff --git a/client/galaxy/scripts/mvc/library/library-folderlist-view.js b/client/galaxy/scripts/mvc/library/library-folderlist-view.js index df4467b5e843..6b8e34ff2764 100644 --- a/client/galaxy/scripts/mvc/library/library-folderlist-view.js +++ b/client/galaxy/scripts/mvc/library/library-folderlist-view.js @@ -277,7 +277,7 @@ var FolderListView = Backbone.View.extend({ // Iterate each checkbox $(':checkbox', '#folder_list_body').each(function() { this.checked = selected; - var $row = $(this.parentElement.parentElement); + var $row = $(this).closest('tr'); // Change color of selected/unselected if (selected) { that.makeDarkRow($row); @@ -295,12 +295,11 @@ var FolderListView = Backbone.View.extend({ var checkbox = ''; var $row; var source; + $row = $(event.target).closest('tr'); if (event.target.localName === 'input'){ checkbox = event.target; - $row = $(event.target.parentElement.parentElement); source = 'input'; } else if (event.target.localName === 'td') { - $row = $(event.target.parentElement); checkbox = $row.find(':checkbox')[0]; source = 'td'; } diff --git a/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js b/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js index 0c301fa1c1b4..5018e3cb2484 100644 --- a/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js +++ b/client/galaxy/scripts/mvc/library/library-foldertoolbar-view.js @@ -205,46 +205,38 @@ var FolderToolbarView = Backbone.View.extend({ return folderDetails.name !== ''; }, - // show bulk import modal modalBulkImport : function(){ var checkedValues = $('#folder_table').find(':checked'); if(checkedValues.length === 0){ mod_toastr.info('You must select some datasets first.'); } else { - this.refreshUserHistoriesList(function(that){ - var template = that.templateBulkImportInModal(); - that.modal = Galaxy.modal; - that.modal.show({ - closing_events : true, - title : 'Import into History', - body : template({histories : that.histories.models}), - buttons : { - 'Import' : function() {that.importAllIntoHistory();}, - 'Close' : function() {Galaxy.modal.hide();} - } - }); + var that = this; + this.histories = new mod_library_model.GalaxyHistories(); + this.histories.fetch() + .done(function(){ + var template = that.templateBulkImportInModal(); + that.modal = Galaxy.modal; + that.modal.show({ + closing_events : true, + title : 'Import into History', + body : template({histories : that.histories.models}), + buttons : { + 'Import' : function() {that.importAllIntoHistory();}, + 'Close' : function() {Galaxy.modal.hide();} + } }); + }) + .fail(function(model, response){ + if (typeof response.responseJSON !== "undefined"){ + mod_toastr.error(response.responseJSON.err_msg); + } else { + mod_toastr.error('An error ocurred.'); + } + }); } }, - refreshUserHistoriesList: function(callback){ - var that = this; - this.histories = new mod_library_model.GalaxyHistories(); - this.histories.fetch({ - success: function (){ - callback(that); - }, - error: function(model, response){ - if (typeof response.responseJSON !== "undefined"){ - mod_toastr.error(response.responseJSON.err_msg); - } else { - mod_toastr.error('An error ocurred.'); - } - } - }); - }, - /** * Import all selected datasets into history. */ @@ -253,7 +245,7 @@ var FolderToolbarView = Backbone.View.extend({ var new_history_name = this.modal.$('input[name=history_name]').val(); var that = this; if (new_history_name !== ''){ - $.post( Galaxy.root + 'api/histories', {name: new_history_name}) + $.post(Galaxy.root + 'api/histories', {name: new_history_name}) .done(function( new_history ) { that.options.last_used_history_id = new_history.id; that.processImportToHistory(new_history.id, new_history.name); @@ -277,10 +269,11 @@ var FolderToolbarView = Backbone.View.extend({ var dataset_ids = []; var folder_ids = []; $('#folder_table').find(':checked').each(function(){ - if ($(this.parentElement.parentElement).data('id') !== '' && this.parentElement.parentElement.classList.contains('dataset_row') ) { - dataset_ids.push($(this.parentElement.parentElement).data('id')); - } else if ($(this.parentElement.parentElement).data('id') !== '' && this.parentElement.parentElement.classList.contains('folder_row') ) { - folder_ids.push($(this.parentElement.parentElement).data('id')); + var row_id = $(this).closest('tr').data('id'); + if (row_id.substring(0,1) == 'F'){ + folder_ids.push(row_id); + } else { + dataset_ids.push(row_id); } }); // prepare the dataset objects to be imported @@ -331,10 +324,11 @@ var FolderToolbarView = Backbone.View.extend({ var dataset_ids = []; var folder_ids = []; $( '#folder_table' ).find( ':checked' ).each( function(){ - if ( $(this.parentElement.parentElement).data('id') !== '' && this.parentElement.parentElement.classList.contains('dataset_row') ) { - dataset_ids.push( $(this.parentElement.parentElement).data('id') ); - } else if ( $(this.parentElement.parentElement).data('id') !== '' && this.parentElement.parentElement.classList.contains('folder_row') ) { - folder_ids.push( $(this.parentElement.parentElement).data('id') ); + var row_id = $(this).closest('tr').data('id'); + if (row_id.substring(0,1) == 'F'){ + folder_ids.push(row_id); + } else { + dataset_ids.push(row_id); } } ); var url = Galaxy.root + 'api/libraries/datasets/download/' + format; @@ -369,33 +363,36 @@ var FolderToolbarView = Backbone.View.extend({ }, addFilesFromHistoryModal: function(){ - this.refreshUserHistoriesList( function( self ){ - self.modal = Galaxy.modal; - var template_modal = self.templateAddFilesFromHistory(); - var folder_name = self.options.full_path[self.options.full_path.length - 1][1] - self.modal.show({ - closing_events : true, - title : 'Adding datasets from your history to folder ' + folder_name, - body : template_modal({histories: self.histories.models}), - buttons : { - 'Add' : function() {self.addAllDatasetsFromHistory();}, - 'Close' : function() {Galaxy.modal.hide();} - }, - closing_callback: function(){ - Galaxy.libraries.library_router.back(); - } - }); - - // user should always have a history, even anonymous user - if (self.histories.models.length > 0){ + this.histories = new mod_library_model.GalaxyHistories(); + var self = this; + this.histories.fetch() + .done(function(){ + self.modal = Galaxy.modal; + var template_modal = self.templateAddFilesFromHistory(); + self.modal.show({ + closing_events : true, + title : 'Adding datasets from your history', + body : template_modal({histories: self.histories.models}), + buttons : { + 'Add' : function() {self.addAllDatasetsFromHistory();}, + 'Close' : function() {Galaxy.modal.hide();} + }, + closing_callback: function(){ + Galaxy.libraries.library_router.navigate('folders/' + self.id, {trigger: true}); + } + }); self.fetchAndDisplayHistoryContents(self.histories.models[0].id); $( "#dataset_add_bulk" ).change(function(event) { self.fetchAndDisplayHistoryContents(event.target.value); }); - } else { - mod_toastr.error( 'An error ocurred.' ); - } - }); + }) + .fail(function(model, response){ + if (typeof response.responseJSON !== "undefined"){ + mod_toastr.error(response.responseJSON.err_msg); + } else { + mod_toastr.error('An error ocurred.'); + } + }); }, /** @@ -528,12 +525,10 @@ var FolderToolbarView = Backbone.View.extend({ that.renderJstree( options ); $('.jstree-folders-message').hide(); $('.jstree-preserve-structure').hide(); - $('.jstree-link-files').hide(); $('.jstree-files-message').show(); } else if ( event.target.value ==='jstree-disable-files' ){ $('.jstree-files-message').hide(); $('.jstree-folders-message').show(); - $('.jstree-link-files').show(); $('.jstree-preserve-structure').show(); options.disabled_jstree_element = 'files'; that.renderJstree( options ); @@ -606,6 +601,9 @@ var FolderToolbarView = Backbone.View.extend({ importFromPathsClicked: function(){ var preserve_dirs = this.modal.$el.find('.preserve-checkbox').is(':checked'); var link_data = this.modal.$el.find('.link-checkbox').is(':checked'); + var space_to_tab = this.modal.$el.find('.spacetab-checkbox').is(':checked'); + var to_posix_lines = this.modal.$el.find('.posix-checkbox').is(':checked'); + var tag_using_filenames = this.modal.$el.find('.tag-files').is(':checked'); var file_type = this.select_extension.value(); var dbkey = this.select_genome.value(); var paths = $('textarea#import_paths').val(); @@ -625,8 +623,11 @@ var FolderToolbarView = Backbone.View.extend({ this.chainCallImportingFolders( { paths: valid_paths, preserve_dirs: preserve_dirs, link_data: link_data, + space_to_tab: space_to_tab, + to_posix_lines: to_posix_lines, source: 'admin_path', file_type: file_type, + tag_using_filenames: tag_using_filenames, dbkey: dbkey } ); } }, @@ -678,6 +679,8 @@ var FolderToolbarView = Backbone.View.extend({ var selected_nodes = _.filter(all_nodes, function(node){ return node.state.disabled == false; }) var preserve_dirs = this.modal.$el.find( '.preserve-checkbox' ).is( ':checked' ); var link_data = this.modal.$el.find( '.link-checkbox' ).is( ':checked' ); + var space_to_tab = this.modal.$el.find('.spacetab-checkbox').is(':checked'); + var to_posix_lines = this.modal.$el.find('.posix-checkbox').is(':checked'); var file_type = this.select_extension.value(); var dbkey = this.select_genome.value(); var tag_using_filenames = this.modal.$el.find( '.tag-files' ).is( ':checked' ); @@ -698,6 +701,8 @@ var FolderToolbarView = Backbone.View.extend({ this.chainCallImportingFolders( { paths: paths, preserve_dirs: preserve_dirs, link_data: link_data, + space_to_tab: space_to_tab, + to_posix_lines: to_posix_lines, source: full_source, file_type: file_type, dbkey: dbkey, @@ -707,6 +712,9 @@ var FolderToolbarView = Backbone.View.extend({ this.chainCallImportingUserdirFiles( { paths : paths, file_type: file_type, dbkey: dbkey, + link_data: link_data, + space_to_tab: space_to_tab, + to_posix_lines: to_posix_lines, source: full_source, tag_using_filenames: tag_using_filenames } ); } @@ -721,6 +729,12 @@ var FolderToolbarView = Backbone.View.extend({ var history_contents_template = self.templateHistoryContents(); self.histories.get(history_id).set({'contents' : history_contents}); self.modal.$el.find('#selected_history_content').html(history_contents_template({history_contents: history_contents.models.reverse()})); + self.modal.$el.find('.history-import-select-all').bind("click", function(){ + $('#selected_history_content [type=checkbox]').prop('checked', true); + }); + self.modal.$el.find('.history-import-unselect-all').bind("click", function(){ + $('#selected_history_content [type=checkbox]').prop('checked', false); + }); }, error: function(model, response){ if (typeof response.responseJSON !== "undefined"){ @@ -745,9 +759,9 @@ var FolderToolbarView = Backbone.View.extend({ } else { this.modal.disableButton( 'Add' ); checked_hdas.each(function(){ - var hid = $( this.parentElement ).data( 'id' ); + var hid = $(this).closest('li').data( 'id' ); if ( hid ) { - var item_type = $( this.parentElement ).data( 'name' ); + var item_type = $(this).closest('li').data( 'name' ); history_item_ids.push( hid ); history_item_types.push( item_type ); } @@ -808,7 +822,6 @@ var FolderToolbarView = Backbone.View.extend({ * @param {boolean} tag_using_filenames add tags to datasets using names of files */ chainCallImportingUserdirFiles: function( options ){ - var that = this; var popped_item = options.paths.pop(); if ( typeof popped_item === "undefined" ) { @@ -824,6 +837,9 @@ var FolderToolbarView = Backbone.View.extend({ '&source=' + options.source + '&path=' + popped_item + '&file_type=' + options.file_type + + '&link_data=' + options.link_data + + '&space_to_tab=' + options.space_to_tab + + '&to_posix_lines=' + options.to_posix_lines + '&dbkey=' + options.dbkey + '&tag_using_filenames=' + options.tag_using_filenames ) ) promise.done( function( response ){ @@ -838,11 +854,13 @@ var FolderToolbarView = Backbone.View.extend({ }, /** - * Take the array of paths and createa request for each of them - * calling them in chain. Update the progress bar in between each. + * Take the array of paths and create a request for each of them + * calling them in series. Update the progress bar in between each. * @param {array} paths paths relative to Galaxy root folder * @param {boolean} preserve_dirs indicates whether to preserve folder structure * @param {boolean} link_data copy files to Galaxy or link instead + * @param {boolean} to_posix_lines convert line endings to POSIX standard + * @param {boolean} space_to_tab convert spaces to tabs * @param {str} source string representing what type of folder * is the source of import * @param {boolean} tag_using_filenames add tags to datasets using names of files @@ -866,6 +884,8 @@ var FolderToolbarView = Backbone.View.extend({ '&path=' + popped_item + '&preserve_dirs=' + options.preserve_dirs + '&link_data=' + options.link_data + + '&to_posix_lines=' + options.to_posix_lines + + '&space_to_tab=' + options.space_to_tab + '&file_type=' + options.file_type + '&dbkey=' + options.dbkey + '&tag_using_filenames=' + options.tag_using_filenames ) ) @@ -997,11 +1017,12 @@ var FolderToolbarView = Backbone.View.extend({ var dataset_ids = []; var folder_ids = []; checkedValues.each(function(){ - if ($(this.parentElement.parentElement).data('id') !== undefined) { - if ($(this.parentElement.parentElement).data('id').substring(0,1) == 'F'){ - folder_ids.push($(this.parentElement.parentElement).data('id')); + var row_id = $(this).closest('tr').data('id'); + if (row_id !== undefined) { + if (row_id.substring(0,1) == 'F'){ + folder_ids.push(row_id); } else { - dataset_ids.push($(this.parentElement.parentElement).data('id')); + dataset_ids.push(row_id); } } }); @@ -1162,7 +1183,7 @@ var FolderToolbarView = Backbone.View.extend({ '', ' Details', '', - '', + '', '', '
', '', '', '
', // append jstree object here @@ -1354,6 +1383,7 @@ var FolderToolbarView = Backbone.View.extend({ 'Type: ', 'Genome: ', '
', + '
', '
', '
' ].join('')); }, @@ -1393,7 +1438,7 @@ var FolderToolbarView = Backbone.View.extend({ return _.template([ '
', '
', - 'Select history: ', + '1. Select history: ', ' <%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %> (Dataset Collection)', + '
', - '', - '

', - 'This dataset is unrestricted so everybody can access it. Just share the URL of this page. ', - ' ', - '

', - '
', - '', - '', - '', - '', - '', - '<% if (item.get("file_ext")) { %>', - '', - '', - '', - '', - '<% } %>', - '
">', - 'Name', - '', - '<%= _.escape(item.get("name")) %>', - '
Data type', - '<%= _.escape(item.get("file_ext")) %>', - '
', - '
', - '
', - ].join('')); - }, - templateLibraryPermissions : function(){ return _.template([ '
', diff --git a/client/galaxy/scripts/mvc/library/library-librarytoolbar-view.js b/client/galaxy/scripts/mvc/library/library-librarytoolbar-view.js index 3ea68110cdbb..5f005d7d6487 100644 --- a/client/galaxy/scripts/mvc/library/library-librarytoolbar-view.js +++ b/client/galaxy/scripts/mvc/library/library-librarytoolbar-view.js @@ -210,7 +210,7 @@ var LibraryToolbarView = Backbone.View.extend({ '', '', '<% } %>', - '', + '', '', '', '', diff --git a/client/galaxy/scripts/mvc/tag.js b/client/galaxy/scripts/mvc/tag.js index 0809f16b3573..8335f36d5c66 100644 --- a/client/galaxy/scripts/mvc/tag.js +++ b/client/galaxy/scripts/mvc/tag.js @@ -11,29 +11,40 @@ var TagsEditor = Backbone.View .extend( baseMVC.LoggableMixin ) .extend( baseMVC.HiddenUntilActivatedViewMixin ).extend({ - tagName : 'div', - className : 'tags-display', + tagName : 'div', + className : 'tags-display', + select_width : '100%', + events: {}, /** Set up listeners, parse options */ initialize : function( options ){ //console.debug( this, options ); // only listen to the model only for changes to tags - re-render + this.show_editor = false; if (options.usePrompt === false) { this.label = ''; } else { this.label = ''; } + this.workflow_mode = options.workflow_mode || false; + if (this.workflow_mode) { + this.events.click = 'showEditor'; + this.events.keydown = 'keydownHandler'; + } this.hiddenUntilActivated( options.$activator, options ); }, /** Build the DOM elements, call select to on the created input, and set up behaviors */ render : function(){ var self = this; - this.$el.html( this._template() ); - + if (this.workflow_mode) { + this.$el.html(this._workflowTemplate()); + } else { + this.$el.html(this._defaultTemplate()); + } this.$input().select2({ placeholder : 'Add tags', - width : '100%', + width : this.workflow_mode ? this.width : this.select_width, tags : function(){ // initialize possible tags in the dropdown based on all the tags the user has used so far return self._getTagsUsed(); @@ -59,14 +70,61 @@ var TagsEditor = Backbone.View }, /** @returns {String} the html text used to build the view's DOM */ - _template : function(){ + _defaultTemplate : function(){ return [ this.label, - // set up initial tags by adding as CSV to input vals (necc. to init select2) - '' + this._renderEditor() ].join( '' ); }, + _workflowTemplate : function(){ + // Shows labels by default, event handler controls whether we show tags or editor + return [ + this.show_editor ? this._renderEditor() : this._renderTags(), + ].join( ' ' ); + }, + + keydownHandler : function (e) { + switch (e.which) { + // esc + case 27 : + // hide the tag editor when pressing escape + this.hideEditor(); + break; + } + }, + + showEditor: function() { + this.show_editor = true; + this.render(); + }, + + hideEditor: function() { + this.show_editor = false; + this.render(); + }, + + _renderEditor: function(){ + // set up initial tags by adding as CSV to input vals (necc. to init select2) + return '' + }, + + _renderTags : function(){ + var tags = this.model.get('tags'); + var addButton = 'static/images/fugue/tag--plus.png'; + var renderedArray = []; + _.each(tags, function(tag) { + tag = tag.indexOf("name:") == 0 ? tag.slice(5) : tag ; + var renderString = '' + tag + ''; + renderedArray.push( renderString ); + }); + if (renderedArray.length === 0) { + // If there are no tags to render we just show the add-tag-button + renderedArray.push(''); + } + return renderedArray.join(" "); + }, + /** @returns {String} the sorted, comma-separated tags from the model */ tagsToCSV : function(){ var self = this; @@ -132,8 +190,7 @@ var TagsEditor = Backbone.View toString : function(){ return [ 'TagsEditor(', this.model + '', ')' ].join(''); } }); -// ============================================================================= return { - TagsEditor : TagsEditor + TagsEditor : TagsEditor, }; }); diff --git a/client/galaxy/scripts/mvc/tours.js b/client/galaxy/scripts/mvc/tours.js index 012e9fd4b7ca..bf1448317aba 100644 --- a/client/galaxy/scripts/mvc/tours.js +++ b/client/galaxy/scripts/mvc/tours.js @@ -77,6 +77,7 @@ define(['libs/bootstrap-tour'],function(BootstrapTour) { }); }; var ToursView = Backbone.View.extend({ + title: "Tours", // initialize initialize: function() { var self = this; diff --git a/client/galaxy/scripts/mvc/user/user-preferences.js b/client/galaxy/scripts/mvc/user/user-preferences.js index 62edb56cd431..3d867d8d4878 100644 --- a/client/galaxy/scripts/mvc/user/user-preferences.js +++ b/client/galaxy/scripts/mvc/user/user-preferences.js @@ -91,7 +91,7 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc', 'utils/query-string-parsing' ] /** View of the main user preference panel with links to individual user forms */ var View = Backbone.View.extend({ - + title: "User Preferences", initialize: function() { this.model = new Model(); this.setElement( '
' ); diff --git a/client/galaxy/scripts/mvc/workflow/workflow-model.js b/client/galaxy/scripts/mvc/workflow/workflow-model.js new file mode 100644 index 000000000000..613ca4f6ce9f --- /dev/null +++ b/client/galaxy/scripts/mvc/workflow/workflow-model.js @@ -0,0 +1,44 @@ +define([ + "mvc/base-mvc", +], function( baseMVC ){ +/* global Backbone */ +// workflow model + +var logNamespace = 'workflow'; +//============================================================================== +/** @class model for a single workflow. + * @name WorkflowItem + * @augments Backbone.Model + */ +var WorkflowItem = Backbone.Model.extend( baseMVC.LoggableMixin ).extend({ + _logNamespace : logNamespace, + + urlRoot: '/api/workflows', + + toJSON: function(){ + // need to overwrite this as endpoint expects the 'workflow' key in payload + return {workflow : this.attributes}; + }, + +}); + +//============================================================================== +/** @class collection for workflows. + * @name WorkflowCollection + * @augments Backbone.Collection + */ +var WorkflowCollection = Backbone.Collection.extend({ + model: WorkflowItem, + url: '/api/workflows', + + }); + +//============================================================================== + +return { + WorkflowItem: WorkflowItem, + WorkflowCollection: WorkflowCollection, +}; + + +}); diff --git a/client/galaxy/scripts/mvc/workflow/workflow-terminals.js b/client/galaxy/scripts/mvc/workflow/workflow-terminals.js index 45c2cf8f6296..edfb9139245a 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow-terminals.js +++ b/client/galaxy/scripts/mvc/workflow/workflow-terminals.js @@ -476,11 +476,29 @@ define(['mvc/workflow/workflow-globals'], function( Globals ) { initialize: function( attr ) { Terminal.prototype.initialize.call( this, attr ); this.datatypes = attr.datatypes; - this.collectionType = new CollectionTypeDescription( attr.collection_type ); - this.isCollection = true; + if( attr.collection_type ) { + this.collectionType = new CollectionTypeDescription( attr.collection_type ); + } else { + var collectionTypeSource = attr.collection_type_source; + if( ! collectionTypeSource ) { + console.log("Warning: No collection type or collection type source defined."); + } + this.collectionType = ANY_COLLECTION_TYPE_DESCRIPTION; + } + this.isCollection = true; }, update: function( output ) { - var newCollectionType = new CollectionTypeDescription( output.collection_type ); + var newCollectionType; + if( output.collection_type ) { + newCollectionType = new CollectionTypeDescription( output.collection_type ); + } else { + var collectionTypeSource = output.collection_type_source; + if( ! collectionTypeSource ) { + console.log("Warning: No collection type or collection type source defined."); + } + newCollectionType = ANY_COLLECTION_TYPE_DESCRIPTION; + } + if( newCollectionType.collectionType != this.collectionType.collectionType ) { _.each( this.connectors, function( connector ) { // TODO: consider checking if connection valid before removing... diff --git a/client/galaxy/scripts/mvc/workflow/workflow-view-terminals.js b/client/galaxy/scripts/mvc/workflow/workflow-view-terminals.js index 0e0f1398d79a..67351bf117e3 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow-view-terminals.js +++ b/client/galaxy/scripts/mvc/workflow/workflow-view-terminals.js @@ -224,7 +224,8 @@ define(['mvc/workflow/workflow-globals', 'mvc/workflow/workflow-terminals', terminalMappingViewClass: TerminalMappingView, terminalForOutput: function( output ) { var collection_type = output.collection_type; - var terminal = new Terminals.OutputCollectionTerminal( { element: this.el, collection_type: collection_type, datatypes: output.extensions } ); + var collection_type_source = output.collection_type_source; + var terminal = new Terminals.OutputCollectionTerminal( { element: this.el, collection_type: collection_type, collection_type_source: collection_type_source, datatypes: output.extensions } ); return terminal; } }); diff --git a/client/galaxy/scripts/mvc/workflow/workflow.js b/client/galaxy/scripts/mvc/workflow/workflow.js index 988cabe61441..1986c6299429 100644 --- a/client/galaxy/scripts/mvc/workflow/workflow.js +++ b/client/galaxy/scripts/mvc/workflow/workflow.js @@ -1,94 +1,241 @@ /** Workflow view */ -define( [ 'utils/utils', 'mvc/ui/ui-misc' ], function( Utils, Ui ) { +define( [ "libs/toastr", "mvc/tag", "mvc/workflow/workflow-model" ], function( mod_toastr, TAGS, WORKFLOWS ) { - /** Build messages after user action */ - function build_messages() { - var $el_message = this.$( '.response-message' ), - response = {}; - response = { - 'status': Utils.getQueryString( 'status' ), - 'message': _.escape( Utils.getQueryString( 'message' ) ), - 'persistent': true, - 'cls': Utils.getQueryString( 'status' ) + 'message' - }; - $el_message.empty().html( new Ui.Message( response ).$el ); - } - - /** View of the main workflow list page */ - var View = Backbone.View.extend({ + /** View of the individual workflows */ + var WorkflowItemView = Backbone.View.extend({ + tagName: 'tr', // name of (orphan) root tag in this.el + initialize: function(){ + _.bindAll(this, 'render', '_rowTemplate', 'renderTagEditor', '_templateActions', 'removeWorkflow', 'copyWorkflow'); // every function that uses 'this' as the current object should be in here + mod_toastr.options.timeOut = 1500; + }, - initialize: function() { - this.setElement( '
' ); - this.render(); + events: { + 'click #show-in-tool-panel': 'showInToolPanel', + 'click #delete-workflow' : 'removeWorkflow', + 'click #rename-workflow' : 'renameWorkflow', + 'click #copy-workflow' : 'copyWorkflow', }, - render: function() { - var self = this, - min_query_length = 3; - $.getJSON( Galaxy.root + 'api/workflows/', function( workflows ) { - var $el_workflow = null; - // Add workflow header - self.$el.empty().append( self._templateHeader() ); - // Add user actions message if any - build_messages(); - $el_workflow = self.$( '.user-workflows' ); - // Add the actions buttons - $el_workflow.append( self._templateActionButtons() ); - if( workflows.length > 0) { - $el_workflow.append( self._templateWorkflowTable( self, workflows) ); - self.adjust_actiondropdown( $el_workflow ); - // Register delete and run workflow events - _.each( workflows, function( wf ) { - self.confirm_delete( wf ); - }); - self.register_show_tool_menu(); - // Register search workflow event - self.search_workflow( self.$( '.search-wf' ), self.$( '.workflow-search tr' ), min_query_length ); - } - else { - $el_workflow.append( self._templateNoWorkflow() ); - } - }); + render: function(){ + $(this.el).html(this._rowTemplate()); + return this; }, - // Save the workflow as an item in Tool panel - register_show_tool_menu: function() { - var $el_checkboxes = this.$( '.show-in-tool-panel' ); - $el_checkboxes.on( 'click', function( e ) { - var ids = []; - // Look for all the checked checkboxes - for( var item = 0; item < $el_checkboxes.length; item++ ) { - var checkbox = $el_checkboxes[ item ]; - if( checkbox.checked ) { - ids.push( checkbox.value ); + showInToolPanel: function(){ + this.model.set('show_in_tool_panel', !this.model.get('show_in_tool_panel')); + this.model.save(); + // This reloads the whole page, so that the workflow appears in the tool panel. + // Ideally we would notify only the tool panel of a change + window.location = Galaxy.root + 'workflow'; + }, + + removeWorkflow: function(){ + var wfName = this.model.get('name'); + if (confirm( "Are you sure you want to delete workflow '" + wfName + "'?" )) { + this.model.destroy({ + success: function() { + mod_toastr.success("Successfully deleted workflow '" + wfName + "'"); + } + }); + this.remove(); + }; + }, + + renameWorkflow: function(){ + var oldName = this.model.get('name'); + var newName = prompt("Enter a new Name for workflow '" + oldName + "'", oldName ); + if (newName) { + this.model.save( + { 'name': newName }, + { success: function() { + mod_toastr.success("Successfully renamed workflow '" + oldName + "' to '" + newName + "'") } + }); + this.render(); + } + }, + + copyWorkflow: function(){ + self = this; + var oldName = this.model.get('name'); + $.getJSON(this.model.urlRoot + '/' + this.model.id + '/download', function(wfJson) { + var newName = 'Copy of ' + oldName; + var currentOwner = self.model.get('owner'); + if (currentOwner != Galaxy.user.attributes.username) { + newName += ' shared by user ' + currentOwner; } - // Save all the checked workflows - $.ajax({ - type: 'PUT', - url: Galaxy.root + 'api/workflows/menu/', - data: JSON.stringify( { 'workflow_ids': ids } ), - contentType : 'application/json' - }).done( function( response ) { - window.location = Galaxy.root + 'workflow'; + wfJson.name = newName; + self.collection.create(wfJson, { at: 0, + wait: true, + success: function() { + mod_toastr.success("Successfully copied workflow '" + oldName + "' to '" + newName + "'") + }, + error : function(model, resp, options) { + // signature seems to have changed over the course of backbone dev + // see https://github.com/jashkenas/backbone/issues/2606#issuecomment-19289483 + mod_toastr.error(options.errorThrown); + } }); + }).error(function(jqXHR, textStatus, errorThrown) { + mod_toastr.error(jqXHR.responseJSON.err_msg); + }) + }, + + _rowTemplate: function() { + var show = this.model.get("show_in_tool_panel"); + var wfId = this.model.id; + var checkboxHtml = ''; + var trHtml = '' + + '' + + '' + + '' + '
' + '' + + '' + ( this.model.get('owner') === Galaxy.user.attributes.username ? "You" : this.model.get('owner') ) +'
' + + '' + this.model.get("number_of_steps") + '' + + '' + ( this.model.get("published") ? "Yes" : "No" ) + '' + + ''+ checkboxHtml + ''; + return trHtml; + }, + + renderTagEditor: function(){ + var TagEditor = new TAGS.TagsEditor({ + model : this.model, + el : $.find( '.' + this.model.id + '.tags-display' ), + workflow_mode : true }); + TagEditor.toggle( true ); + TagEditor.render(); + }, + + /** Template for user actions for workflows */ + _templateActions: function( ) { + if( this.model.get("owner") === Galaxy.user.attributes.username ) { + return ''; + } + else { + return ''; + } + }, + }); + + /** View of the main workflow list page */ + var WorkflowListView = Backbone.View.extend({ + title: "Workflows", + initialize: function() { + this.setElement( '
' ); + _.bindAll(this, 'adjustActiondropdown') + this.collection = new WORKFLOWS.WorkflowCollection(); + this.collection.fetch().done(this.render()); + this.collection.bind('add', this.appendItem); + this.collection.on('sync', this.render, this); + }, + + events: { + 'dragleave' : 'unhighlightDropZone', + 'drop' : 'drop', + 'dragover': function(ev) { + $( '.hidden_description_layer' ).addClass( 'dragover' ); + $('.menubutton').addClass('background-none'); + ev.preventDefault(); + } + }, + + unhighlightDropZone: function() { + $( '.hidden_description_layer' ).removeClass( 'dragover' ); + $('.menubutton').removeClass('background-none'); + }, + + drop: function(e) { + // TODO: check that file is valid galaxy workflow + this.unhighlightDropZone(); + e.preventDefault(); + var files = e.dataTransfer.files; + var self = this; + for (var i = 0, f; f = files[i]; i++) { + self.readWorkflowFiles(f); + } + }, + + readWorkflowFiles: function(f) { + var self = this; + var reader = new FileReader(); + reader.onload = function(theFile) { + try { + var wf_json = JSON.parse(reader.result); + } catch(e) { + mod_toastr.error("Could not read file '" + f.name + "'. Verify it is a valid Galaxy workflow"); + wf_json = null; + } + if (wf_json) { + self.collection.create(wf_json, { + at: 0, + wait: true, + success: function() { + mod_toastr.success("Successfully imported workflow '" + wf_json.name + "'") + }, + error : function(model, resp, options) { + mod_toastr.error(options.errorThrown); + } + }); + } + }; + reader.readAsText(f, 'utf-8'); + }, + + render: function() { + // Add workflow header + var header = this._templateHeader(); + // Add the actions buttons + var templateActions = this._templateActionButtons(); + var tableTemplate = this._templateWorkflowTable(); + this.$el.html( header + templateActions + tableTemplate); + var self = this; + _(this.collection.models).each(function(item){ // in case collection is not empty + self.appendItem(item); + self.confirmDelete(item); + }, this); + var minQueryLength = 3; + this.searchWorkflow( this.$( '.search-wf' ), this.$( '.workflow-search tr' ), minQueryLength ); + this.adjustActiondropdown(); + return this; + }, + + appendItem: function(item){ + var workflowItemView = new WorkflowItemView({ + model: item, + collection: this.collection, }); + $( '.workflow-search' ).append(workflowItemView.render().el); + workflowItemView.renderTagEditor(); }, /** Add confirm box before removing/unsharing workflow */ - confirm_delete: function( workflow ) { - var $el_wf_link = this.$( '.link-confirm-' + workflow.id ), - $el_shared_wf_link = this.$( '.link-confirm-shared-' + workflow.id ); - $el_wf_link.click( function() { - return confirm( "Are you sure you want to delete workflow '" + workflow.name + "'?" ); - }); + confirmDelete: function( workflow ) { + var $el_shared_wf_link = this.$( '.link-confirm-shared-' + workflow.id ); $el_shared_wf_link.click( function() { - return confirm( "Are you sure you want to remove the shared workflow '" + workflow.name + "'?" ); + return confirm( "Are you sure you want to remove the shared workflow '" + workflow.attributes.name + "'?" ); }); }, /** Implement client side workflow search/filtering */ - search_workflow: function( $el_searchinput, $el_tabletr, min_querylen ) { + searchWorkflow: function( $el_searchinput, $el_tabletr, min_querylen ) { $el_searchinput.on( 'keyup', function () { var query = $( this ).val(); // Filter when query is at least 3 characters @@ -110,13 +257,12 @@ define( [ 'utils/utils', 'mvc/ui/ui-misc' ], function( Utils, Ui ) { }, /** Ajust the position of dropdown with respect to table */ - adjust_actiondropdown: function( $el ) { - $el.on( 'show.bs.dropdown', function () { - $el.css( "overflow", "inherit" ); + adjustActiondropdown: function( ) { + $(this.el).on( 'show.bs.dropdown', function () { + $(this.el).css( "overflow", "inherit" ); }); - - $el.on( 'hide.bs.dropdown', function () { - $el.css( "overflow", "auto" ); + $(this.el).on( 'hide.bs.dropdown', function () { + $(this.el).css( "overflow", "auto" ); }); }, @@ -143,57 +289,17 @@ define( [ 'utils/utils', 'mvc/ui/ui-misc' ], function( Utils, Ui ) { }, /** Template for workflow table */ - _templateWorkflowTable: function( self, workflows ) { - var tableHtml = "", trHtml = ""; - tableHtml = tableHtml + '' + + _templateWorkflowTable: function( ) { + var tableHtml = '
' + '' + '' + + '' + '' + '' + '' + '' + ''; - _.each( workflows, function( wf ) { - var checkbox_html = ''; - trHtml = trHtml + '' + - '' + - '' + - '' + - '' + - '' + - ''; - }); - return tableHtml + '' + trHtml + '
NameTagsOwner# of StepsPublishedShow in tools panel
' + - '' + - '' + ( wf.owner === Galaxy.user.attributes.username ? "You" : wf.owner ) +'' + wf.number_of_steps + '' + ( wf.published ? "Yes" : "No" ) + ''+ checkbox_html +'
'; - }, - - /** Template for user actions for workflows */ - _templateActions: function( workflow ) { - if( workflow.owner === Galaxy.user.attributes.username ) { - return ''; - } - else { - return ''; - } + return tableHtml + '

Drop workflow files here to import

' + '
'; }, /** Main template */ @@ -264,11 +370,10 @@ define( [ 'utils/utils', 'mvc/ui/ui-misc' ], function( Utils, Ui ) { "
" + "
"; }, - }); return { - View : View, + View : WorkflowListView, ImportWorkflowView : ImportWorkflowView }; }); diff --git a/client/galaxy/scripts/utils/uploadbox.js b/client/galaxy/scripts/utils/uploadbox.js index 1382c3179bec..ec6d20753341 100755 --- a/client/galaxy/scripts/utils/uploadbox.js +++ b/client/galaxy/scripts/utils/uploadbox.js @@ -65,11 +65,14 @@ if (xhr.readyState == xhr.DONE) { // parse response var response = null; + var extra_info = ""; if (xhr.responseText) { try { response = jQuery.parseJSON(xhr.responseText); + extra_info = response.err_msg; } catch (e) { response = xhr.responseText; + extra_info = response; } } // pass any error to the error option @@ -82,7 +85,7 @@ } else if (!text) { text = cnf.error_default; } - cnf.error(text + ' (' + xhr.status + ')'); + cnf.error(text + ' (' + xhr.status + '). ' + extra_info); } else { cnf.success(response); } diff --git a/client/galaxy/scripts/utils/utils.js b/client/galaxy/scripts/utils/utils.js index d90311677362..0ab0e5e4ef04 100644 --- a/client/galaxy/scripts/utils/utils.js +++ b/client/galaxy/scripts/utils/utils.js @@ -2,7 +2,7 @@ * Galaxy utilities comprises small functions, which at this point * do not require their own classes/files */ -define( [], function() { +define( ['utils/localization'], function(_l) { /** Builds a basic iframe */ function iframe( src ) { @@ -53,7 +53,7 @@ define( [], function() { return /^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@'). replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). replace(/(?:^|:|,)(?:\s*\[)+/g, '')); - }; + } /** * Sanitize/escape a string @@ -61,7 +61,7 @@ define( [], function() { */ function sanitize(content) { return $('
').text(content).html(); - }; + } /** * Checks if a value or list of values is `empty` @@ -81,7 +81,7 @@ define( [], function() { } } return false; - }; + } /** * Convert list to pretty string @@ -97,7 +97,7 @@ define( [], function() { return lst; } return ''; - }; + } /** * Request handler for GET @@ -125,7 +125,7 @@ define( [], function() { } }); } - }; + } /** * Request handler @@ -142,7 +142,7 @@ define( [], function() { type : options.type || 'GET', data : options.data || {}, url : options.url - } + }; // encode data into url if ( ajaxConfig.type == 'GET' || ajaxConfig.type == 'DELETE' ) { if ( !$.isEmptyObject(ajaxConfig.data) ) { @@ -178,7 +178,7 @@ define( [], function() { }).always(function() { options.complete && options.complete(); }); - }; + } /** * Read a property value from CSS @@ -191,7 +191,7 @@ define( [], function() { var value = el.css(name); el.remove(); return value; - }; + } /** * Load a CSS file @@ -201,7 +201,7 @@ define( [], function() { if (!$('link[href^="' + url + '"]').length) { $('').appendTo('head'); } - }; + } /** * Safely merge to dictionaries @@ -214,7 +214,7 @@ define( [], function() { } else { return optionsDefault; } - }; + } /** @@ -257,13 +257,13 @@ define( [], function() { } else { return '' + rounded + ' ' + unit; } - }; + } /** Create a unique id */ function uid(){ top.__utils__uid__ = top.__utils__uid__ || 0; return 'uid-' + top.__utils__uid__++; - }; + } /** Create a time stamp */ function time() { @@ -275,7 +275,7 @@ define( [], function() { + d.getFullYear() + ", " + hours + ":" + minutes; - }; + } /** Append script and style tags to Galaxy main application */ function appendScriptStyle( data ) { @@ -287,12 +287,20 @@ define( [], function() { if( data.styles && data.styles !== "" ) { $( '