Merged galaxy/galaxy-central into default

This commit is contained in:
Carl Eberhard
2015-01-12 10:18:56 -05:00
53 changed files with 1529 additions and 1212 deletions
@@ -805,7 +805,7 @@ var Node = Backbone.Model.extend({
nodeView.addDataOutput( output );
} );
nodeView.render();
workflow.node_changed( this );
workflow.node_changed( this, true);
},
update_field_data : function( data ) {
var node = this;
@@ -1094,9 +1094,9 @@ $.extend( Workflow.prototype, {
this.active_node = node;
}
},
node_changed : function ( node ) {
node_changed : function ( node, force ) {
this.has_changes = true;
if ( this.active_node == node ) {
if ( this.active_node == node && (!parent.__NEWTOOLFORM__ || force)) {
// Reactive with new form_html
this.check_changes_in_active_form(); //Force changes to be saved even on new connection (previously dumped)
parent.show_form_for_tool( node.form_html + node.tooltip, node );
@@ -1305,6 +1305,12 @@ function add_node( type, title_text, tool_id ) {
return node;
}
function update_node( data ) {
var node = workflow.active_node;
if (node) {
node.update_field_data(data, true);
}
}
var ext_to_type = null;
var type_to_type = null;
@@ -0,0 +1,315 @@
/**
This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules.
*/
define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
'mvc/citation/citation-model', 'mvc/citation/citation-view',
'mvc/tools', 'mvc/tools/tools-template', 'mvc/tools/tools-content', 'mvc/tools/tools-section', 'mvc/tools/tools-tree'],
function(Utils, Deferred, Portlet, Ui, CitationModel, CitationView,
Tools, ToolTemplate, ToolContent, ToolSection, ToolTree) {
// create form view
return Backbone.View.extend({
// initialize
initialize: function(options) {
// log options
console.debug(options);
// link galaxy modal or create one
var galaxy = parent.Galaxy;
if (galaxy && galaxy.modal) {
this.modal = galaxy.modal;
} else {
this.modal = new Ui.Modal.View();
}
// check if the user is an admin
if (galaxy && galaxy.currUser) {
this.is_admin = galaxy.currUser.get('is_admin');
} else {
this.is_admin = false;
}
// link options
this.options = options;
// link container
this.container = this.options.container || 'body';
// create deferred processing queue handler
// this handler reduces the number of requests to the api by filtering redundant requests
this.deferred = new Deferred();
// set element
this.setElement('<div/>');
// add to main element
$(this.container).append(this.$el);
// build this form
this._buildForm();
},
/** Shows the final message (usually upon successful job submission)
*/
reciept: function($el) {
$(this.container).empty();
$(this.container).append($el);
},
/** Highlight and scroll to input element (currently only used for error notifications)
*/
highlight: function (input_id, message, silent) {
// get input field
var input_element = this.element_list[input_id];
// check input element
if (input_element) {
// mark error
input_element.error(message || 'Please verify this parameter.');
// scroll to first input element
if (!silent) {
$(this.container).animate({
scrollTop: input_element.$el.offset().top - 20
}, 500);
}
}
},
/** Main tool form build function. This function is called once a new model is available.
*/
_buildForm: function() {
// link this
var self = this;
// reset events
this.off('refresh');
this.off('reset');
// reset field list, which contains the input field elements
this.field_list = {};
// reset sequential input definition list, which contains the input definitions as provided from the api
this.input_list = {};
// reset input element list, which contains the dom elements of each input element (includes also the input field)
this.element_list = {};
// creates a tree/json data structure from the input form
this.tree = new ToolTree(this);
// request history content and build form
this.content = new ToolContent(this);
// link model options
var options = this.options;
// create ui elements
this._renderForm(options);
// rebuild the underlying data structure
this.tree.finalize();
// show errors
if (!this.workflow && options.errors) {
var error_messages = this.tree.matchResponse(options.errors);
for (var input_id in error_messages) {
this.highlight(input_id, error_messages[input_id], true);
}
}
// add refresh listener
this.on('refresh', function() {
// by using/reseting the deferred ajax queue the number of redundant calls is reduced
self.deferred.reset();
self.deferred.execute(function(){self._updateModel()});
});
// add reset listener
this.on('reset', function() {
for (var i in this.element_list) {
this.element_list[i].reset();
}
});
},
/** Renders the UI elements required for the form
*/
_renderForm: function(options) {
// link this
var self = this;
// create message view
this.message = new Ui.Message();
// button for version selection
var requirements_button = new Ui.ButtonIcon({
icon : 'fa-info-circle',
title : 'Requirements',
tooltip : 'Display tool requirements',
onclick : function() {
if (!this.visible) {
this.visible = true;
self.message.update({
persistent : true,
message : ToolTemplate.requirements(options),
status : 'info'
});
} else {
this.visible = false;
self.message.update({
message : ''
});
}
}
});
if (!options.requirements || options.requirements.length == 0) {
requirements_button.$el.hide();
}
// button for version selection
var versions_button = new Ui.ButtonMenu({
icon : 'fa-cubes',
title : 'Versions',
tooltip : 'Select another tool version'
});
if (options.versions && options.versions.length > 1) {
for (var i in options.versions) {
var version = options.versions[i];
if (version != options.version) {
versions_button.addMenu({
title : 'Switch to ' + version,
version : version,
icon : 'fa-cube',
onclick : function() {
// here we update the tool version (some tools encode the version also in the id)
options.id = options.id.replace(options.version, this.version);
options.version = this.version;
// rebuild the model and form
self.deferred.reset();
self.deferred.execute(function(){self._buildModel()});
}
});
}
}
} else {
versions_button.$el.hide();
}
// button menu
var menu_button = new Ui.ButtonMenu({
icon : 'fa-caret-down',
title : 'Options',
tooltip : 'View available options'
});
// configure button selection
if(options.biostar_url) {
// add question option
menu_button.addMenu({
icon : 'fa-question-circle',
title : 'Question?',
tooltip : 'Ask a question about this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/p/new/post/');
}
});
// create search button
menu_button.addMenu({
icon : 'fa-search',
title : 'Search',
tooltip : 'Search help for this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/t/' + options.id + '/');
}
});
};
// create share button
menu_button.addMenu({
icon : 'fa-share',
title : 'Share',
tooltip : 'Share this tool',
onclick : function() {
prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + galaxy_config.root + 'root?tool_id=' + options.id);
}
});
// add admin operations
if (this.is_admin) {
// create download button
menu_button.addMenu({
icon : 'fa-download',
title : 'Download',
tooltip : 'Download this tool',
onclick : function() {
window.location.href = galaxy_config.root + 'api/tools/' + options.id + '/download';
}
});
}
// create tool form section
this.section = new ToolSection.View(self, {
inputs : options.inputs,
cls : 'ui-table-plain'
});
// switch to classic tool form mako if the form definition is incompatible
if (this.incompatible) {
this.$el.hide();
$('#tool-form-classic').show();
return;
}
// create portlet
this.portlet = new Portlet.View({
icon : 'fa-wrench',
title : '<b>' + options.name + '</b> ' + options.description + ' (Galaxy Tool Version ' + options.version + ')',
cls : 'ui-portlet-slim',
operations: {
requirements : requirements_button,
menu : menu_button,
versions : versions_button
},
buttons : this.buttons
});
// append message
this.portlet.append(this.message.$el, true);
// append tool section
this.portlet.append(this.section.$el);
// start form
this.$el.empty();
this.$el.append(this.portlet.$el);
// append help
if (options.help != '') {
this.$el.append(ToolTemplate.help(options.help));
}
// append citations
if (options.citations) {
var $citations = $('<div/>');
var citations = new CitationModel.ToolCitationCollection();
citations.tool_id = options.id;
var citation_list_view = new CitationView.CitationListView({ el: $citations, collection: citations } );
citation_list_view.render();
citations.fetch();
this.$el.append($citations);
}
// show message if available in model
if (options.message) {
this.message.update({
persistent : true,
status : 'warning',
message : options.message
});
}
}
});
});
@@ -0,0 +1,66 @@
/**
This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules.
*/
define(['utils/utils', 'mvc/tools/tools-form-base'],
function(Utils, ToolFormBase) {
// create form view
var View = ToolFormBase.extend({
initialize: function(options) {
this.workflow = true;
ToolFormBase.prototype.initialize.call(this, options);
},
/** Builds a new model through api call and recreates the entire form
*/
_buildModel: function() {
},
/** Request a new model for an already created tool form and updates the form inputs
*/
_updateModel: function() {
// create the request dictionary
var self = this;
var current_state = this.tree.finalize();
// log tool state
console.debug('tools-form-workflow::_refreshForm() - Refreshing states.');
console.debug(current_state);
// register process
var process_id = this.deferred.register();
// build model url for request
var model_url = galaxy_config.root + 'workflow/editor_form_post?tool_id=' + this.options.id;
// post job
Utils.request({
type : 'GET',
url : model_url,
data : current_state,
success : function(node) {
parent.update_node(node);
// process completed
self.deferred.done(process_id);
// log success
console.debug('tools-form::_refreshForm() - States refreshed.');
console.debug(node);
},
error : function(response) {
// process completed
self.deferred.done(process_id);
// log error
console.debug('tools-form::_refreshForm() - Refresh request failed.');
console.debug(response);
}
});
}
});
return {
View: View
};
});
+17 -318
View File
@@ -1,79 +1,28 @@
/**
This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules.
*/
define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
'mvc/citation/citation-model', 'mvc/citation/citation-view',
'mvc/tools', 'mvc/tools/tools-template', 'mvc/tools/tools-content', 'mvc/tools/tools-section', 'mvc/tools/tools-tree', 'mvc/tools/tools-jobs'],
function(Utils, Deferred, Portlet, Ui, CitationModel, CitationView,
Tools, ToolTemplate, ToolContent, ToolSection, ToolTree, ToolJobs) {
define(['utils/utils', 'mvc/ui/ui-misc', 'mvc/tools/tools-form-base', 'mvc/tools/tools-jobs'],
function(Utils, Ui, ToolFormBase, ToolJobs) {
// create form view
var View = Backbone.View.extend({
// base element
container: 'body',
var View = ToolFormBase.extend({
// initialize
initialize: function(options) {
// log options
console.debug(options);
// link galaxy modal or create one
var galaxy = parent.Galaxy;
if (galaxy && galaxy.modal) {
this.modal = galaxy.modal;
} else {
this.modal = new Ui.Modal.View();
}
// check if the user is an admin
if (galaxy && galaxy.currUser) {
this.is_admin = galaxy.currUser.get('is_admin')
} else {
this.is_admin = false;
}
// link options
this.options = options;
// create deferred processing queue handler
// this handler reduces the number of requests to the api by filtering redundant requests
this.deferred = new Deferred();
// set element
this.setElement('<div/>');
// add to main element
$(this.container).append(this.$el);
// build this form
this._buildForm();
},
/** Shows the final message (usually upon successful job submission)
*/
reciept: function($el) {
$(this.container).empty();
$(this.container).append($el);
},
/** Highlight and scroll to input element (currently only used for error notifications)
*/
highlight: function (input_id, message, silent) {
// get input field
var input_element = this.element_list[input_id];
// check input element
if (input_element) {
// mark error
input_element.error(message || 'Please verify this parameter.');
// scroll to first input element
if (!silent) {
$(this.container).animate({
scrollTop: input_element.$el.offset().top - 20
}, 500);
}
var self = this;
this.job_handler = new ToolJobs(this);
this.buttons = {
execute : new Ui.Button({
icon : 'fa-check',
tooltip : 'Execute: ' + options.name,
title : 'Execute',
cls : 'btn btn-primary',
floating : 'clear',
onclick : function() {
self.job_handler.submit();
}
})
}
ToolFormBase.prototype.initialize.call(this, options);
},
/** Builds a new model through api call and recreates the entire form
@@ -250,256 +199,6 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
console.debug(response);
}
});
},
/** Main tool form build function. This function is called once a new model is available.
*/
_buildForm: function() {
// link this
var self = this;
// reset events
this.off('refresh');
this.off('reset');
// reset field list, which contains the input field elements
this.field_list = {};
// reset sequential input definition list, which contains the input definitions as provided from the api
this.input_list = {};
// reset input element list, which contains the dom elements of each input element (includes also the input field)
this.element_list = {};
// creates a tree/json data structure from the input form
this.tree = new ToolTree(this);
// creates the job handler
this.job_handler = new ToolJobs(this);
// request history content and build form
this.content = new ToolContent(this);
// link model options
var options = this.options;
// create ui elements
this._renderForm(options);
// rebuild the underlying data structure
this.tree.finalize();
// show errors
if (options.errors) {
var error_messages = this.tree.matchResponse(options.errors);
for (var input_id in error_messages) {
this.highlight(input_id, error_messages[input_id], true);
}
}
// add refresh listener
this.on('refresh', function() {
// by using/reseting the deferred ajax queue the number of redundant calls is reduced
self.deferred.reset();
self.deferred.execute(function(){self._updateModel()});
});
// add reset listener
this.on('reset', function() {
for (var i in this.element_list) {
this.element_list[i].reset();
}
});
},
/** Renders the UI elements required for the form
*/
_renderForm: function(options) {
// link this
var self = this;
// create message view
this.message = new Ui.Message();
// button for version selection
var requirements_button = new Ui.ButtonIcon({
icon : 'fa-info-circle',
title : 'Requirements',
tooltip : 'Display tool requirements',
onclick : function() {
if (!this.visible) {
this.visible = true;
self.message.update({
persistent : true,
message : ToolTemplate.requirements(options),
status : 'info'
});
} else {
this.visible = false;
self.message.update({
message : ''
});
}
}
});
if (!options.requirements || options.requirements.length == 0) {
requirements_button.$el.hide();
}
// button for version selection
var versions_button = new Ui.ButtonMenu({
icon : 'fa-cubes',
title : 'Versions',
tooltip : 'Select another tool version'
});
if (options.versions && options.versions.length > 1) {
for (var i in options.versions) {
var version = options.versions[i];
if (version != options.version) {
versions_button.addMenu({
title : 'Switch to ' + version,
version : version,
icon : 'fa-cube',
onclick : function() {
// here we update the tool version (some tools encode the version also in the id)
options.id = options.id.replace(options.version, this.version);
options.version = this.version;
// rebuild the model and form
self.deferred.reset();
self.deferred.execute(function(){self._buildModel()});
}
});
}
}
} else {
versions_button.$el.hide();
}
// button menu
var menu_button = new Ui.ButtonMenu({
icon : 'fa-caret-down',
title : 'Options',
tooltip : 'View available options'
});
// configure button selection
if(options.biostar_url) {
// add question option
menu_button.addMenu({
icon : 'fa-question-circle',
title : 'Question?',
tooltip : 'Ask a question about this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/p/new/post/');
}
});
// create search button
menu_button.addMenu({
icon : 'fa-search',
title : 'Search',
tooltip : 'Search help for this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/t/' + options.id + '/');
}
});
};
// create share button
menu_button.addMenu({
icon : 'fa-share',
title : 'Share',
tooltip : 'Share this tool',
onclick : function() {
prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + galaxy_config.root + 'root?tool_id=' + options.id);
}
});
// add admin operations
if (this.is_admin) {
// create download button
menu_button.addMenu({
icon : 'fa-download',
title : 'Download',
tooltip : 'Download this tool',
onclick : function() {
window.location.href = galaxy_config.root + 'api/tools/' + options.id + '/download';
}
});
}
// create tool form section
this.section = new ToolSection.View(self, {
inputs : options.inputs,
cls : 'ui-table-plain'
});
// switch to classic tool form mako if the form definition is incompatible
if (this.incompatible) {
this.$el.hide();
$('#tool-form-classic').show();
return;
}
// create portlet
this.portlet = new Portlet.View({
icon : 'fa-wrench',
title : '<b>' + options.name + '</b> ' + options.description + ' (Galaxy Tool Version ' + options.version + ')',
cls : 'ui-portlet-slim',
operations: {
requirements : requirements_button,
menu : menu_button,
versions : versions_button
},
buttons: {
execute : new Ui.Button({
icon : 'fa-check',
tooltip : 'Execute: ' + options.name,
title : 'Execute',
cls : 'btn btn-primary',
floating : 'clear',
onclick : function() {
self.job_handler.submit();
}
})
}
});
// append message
this.portlet.append(this.message.$el, true);
// append tool section
this.portlet.append(this.section.$el);
// start form
this.$el.empty();
this.$el.append(this.portlet.$el);
// append help
if (options.help != '') {
this.$el.append(ToolTemplate.help(options.help));
}
// append citations
if (options.citations) {
var $citations = $('<div/>');
var citations = new CitationModel.ToolCitationCollection();
citations.tool_id = options.id;
var citation_list_view = new CitationView.CitationListView({ el: $citations, collection: citations } );
citation_list_view.render();
citations.fetch();
this.$el.append($citations);
}
// show message if available in model
if (options.message) {
this.message.update({
persistent : true,
status : 'warning',
message : options.message
});
}
}
});
@@ -98,7 +98,7 @@ return Backbone.Model.extend({
}
// validate non-optional fields
if (!input_def.optional && input_field.validate && !input_field.validate()) {
if (!input_def.optional && input_value == null) {
this.app.highlight(input_id);
return false;
}
@@ -382,6 +382,11 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep
/** Data input field
*/
_fieldData : function(input_def) {
if (this.app.workflow) {
var extensions = Utils.textify(input_def.extensions.toString());
input_def.info = 'Data input \'' + input_def.name + '\' (' + extensions + ')';
return this._fieldHidden(input_def);
}
var self = this;
return new SelectContent.View(this.app, {
id : 'field-' + input_def.id,
@@ -398,6 +403,14 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep
/** Select/Checkbox/Radio options field
*/
_fieldSelect : function (input_def) {
// show text field in workflow
if (this.app.workflow && input_def.is_dynamic) {
if (!Utils.validate(input_def.value)) {
input_def.value = '';
}
return this._fieldText(input_def);
}
// configure options fields
var options = [];
for (var i in input_def.options) {
@@ -475,7 +488,8 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep
*/
_fieldHidden : function(input_def) {
return new Ui.Hidden({
id : 'field-' + input_def.id
id : 'field-' + input_def.id,
info : input_def.info
});
},
@@ -36,14 +36,7 @@ var View = Backbone.View.extend({
this.list = {};
// error messages
var extensions = options.extensions.toString();
if (extensions) {
extensions = extensions.replace(/,/g, ', ');
var pos = extensions.lastIndexOf(', ');
if (pos != -1) {
extensions = extensions.substr(0, pos) + ' or ' + extensions.substr(pos+1);
}
}
var extensions = Utils.textify(options.extensions);
var hda_error = 'No dataset available.';
if (extensions) {
hda_error = 'No ' + extensions + ' dataset available.';
@@ -2,7 +2,7 @@
This class maps the tool form dom to an api compatible javascript dictionary.
*/
// dependencies
define([], function() {
define(['utils/utils'], function(Utils) {
// tool form tree
return Backbone.Model.extend({
@@ -120,7 +120,7 @@ return Backbone.Model.extend({
// handle default value
if (!field.skip) {
if (input.optional && field.validate && !field.validate()) {
if (field.validate && !field.validate(value)) {
value = null;
}
add (job_input_id, input.id, value);
+10 -4
View File
@@ -412,7 +412,7 @@ var Hidden = Backbone.View.extend({
initialize : function(options) {
// configure options
this.options = options;
// create new element
this.setElement(this._template(this.options));
@@ -425,14 +425,20 @@ var Hidden = Backbone.View.extend({
// value
value : function (new_val) {
if (new_val !== undefined) {
this.$el.val(new_val);
this.$('hidden').val(new_val);
}
return this.$el.val();
return this.$('hidden').val();
},
// element
_template: function(options) {
return '<hidden id="' + options.id + '" value="' + options.value + '"/>';
var tmpl = '<div id="' + options.id + '" >';
if (options.info) {
tmpl += '<label>' + options.info + '</label>';
}
tmpl += '<hidden value="' + options.value + '"/>' +
'</div>';
return tmpl;
}
});
+1 -10
View File
@@ -137,16 +137,7 @@ var Base = Backbone.View.extend({
/** Validate the selected option/options
*/
validate: function() {
var current = this.value();
if (!(current instanceof Array)) {
current = [current];
}
for (var i in current) {
if ([null, 'null', undefined].indexOf(current[i]) > -1) {
return false;
}
}
return true;
return Utils.validate(this.value());
},
/** Wait message during request processing
@@ -96,16 +96,7 @@ var View = Backbone.View.extend({
/** Validate the current selection
*/
validate: function() {
var current = this.value();
if (!(current instanceof Array)) {
current = [current];
}
for (var i in current) {
if ([null, 'null', undefined].indexOf(current[i]) > -1) {
return false;
}
}
return true;
return Utils.validate(this.value());
},
/** Return the label/text of the current selection
@@ -104,7 +104,7 @@ return Backbone.View.extend({
});
// define location
$('#left .unified-panel-header-inner').append((new UploadButton.View(this.ui_button)).$el);
$('.with-upload-button').append((new UploadButton.View(this.ui_button)).$el);
// load extension
var self = this;
+37 -1
View File
@@ -14,6 +14,40 @@ function sanitize(content) {
return $('<div/>').text(content).html();
};
/**
* Validate atomic values or list of values
* usually used for selectable options
* @param{String} value - Value or list to be validated
*/
function validate (value) {
if (!(value instanceof Array)) {
value = [value];
}
for (var i in value) {
if (['None', null, 'null', undefined, 'undefined'].indexOf(value[i]) > -1) {
return false;
}
}
return true;
};
/**
* Convert list to pretty string
* @param{String} lst - List of strings to be converted in human readable list sentence
*/
function textify(lst) {
var lst = lst.toString();
if (lst) {
lst = lst.replace(/,/g, ', ');
var pos = lst.lastIndexOf(', ');
if (pos != -1) {
lst = lst.substr(0, pos) + ' or ' + lst.substr(pos+1);
}
return lst;
}
return '';
};
/**
* Request handler for GET
* @param{String} url - Url request is made to
@@ -212,7 +246,9 @@ return {
time: time,
wrap: wrap,
request: request,
sanitize: sanitize
sanitize: sanitize,
textify: textify,
validate: validate
};
});
+331 -1
View File
@@ -33,7 +33,7 @@ from galaxy.tools.actions.upload import UploadToolAction
from galaxy.tools.actions.data_source import DataSourceToolAction
from galaxy.tools.actions.data_manager import DataManagerToolAction
from galaxy.tools.deps import build_dependency_manager
from galaxy.tools.parameters import check_param, params_from_strings, params_to_strings
from galaxy.tools.parameters import params_to_incoming, check_param, params_from_strings, params_to_strings
from galaxy.tools.parameters import output_collect
from galaxy.tools.parameters.basic import (BaseURLToolParameter,
DataToolParameter, HiddenToolParameter, LibraryDatasetToolParameter,
@@ -2073,6 +2073,336 @@ class Tool( object, Dictifiable ):
return tool_dict
def to_json (self, trans, **kwd):
"""
Recursively creates a tool dictionary containing repeats, dynamic options and updated states.
"""
job_id = kwd.get('job_id', None)
dataset_id = kwd.get('dataset_id', None)
# load job details if provided
job = None
if job_id:
try:
job_id = trans.security.decode_id( job_id )
job = trans.sa_session.query( trans.app.model.Job ).get( job_id )
except Exception, exception:
trans.response.status = 500
log.error('Failed to retrieve job.')
return { 'error': 'Failed to retrieve job.' }
elif dataset_id:
try:
dataset_id = trans.security.decode_id( dataset_id )
data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id )
if not ( trans.user_is_admin() or trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), data.dataset ) ):
trans.response.status = 500
log.error('User has no access to dataset.')
return { 'error': 'User has no access to dataset.' }
job = data.creating_job
if not job:
trans.response.status = 500
log.error('Creating job not found.')
return { 'error': 'Creating job not found.' }
except Exception, exception:
trans.response.status = 500
log.error('Failed to get job information.')
return { 'error': 'Failed to get job information.' }
# load job parameters into incoming
tool_message = ''
if job:
try:
job_params = job.get_param_values( trans.app, ignore_errors = True )
job_messages = self.check_and_update_param_values( job_params, trans, update_values=False )
tool_message = self._compare_tool_version(trans, job)
params_to_incoming( kwd, self.inputs, job_params, trans.app )
except Exception, exception:
trans.response.status = 500
return { 'error': str( exception ) }
# create parameter object
params = galaxy.util.Params( kwd, sanitize = False )
# convert value to jsonifiable value
def jsonify(v):
# check if value is numeric
isnumber = False
try:
float(v)
isnumber = True
except Exception:
pass
# fix hda parsing
if isinstance(v, trans.app.model.HistoryDatasetAssociation):
return {
'id' : trans.security.encode_id(v.id),
'src' : 'hda'
}
elif isinstance(v, bool):
if v is True:
return 'true'
else:
return 'false'
elif isinstance(v, basestring) or isnumber:
return v
else:
return None
# ensures that input dictionary is jsonifiable
def sanitize(dict):
# get current value
value = dict['value'] if 'value' in dict else None
# jsonify by type
if dict['type'] in ['data']:
if isinstance(value, list):
value = [ jsonify(v) for v in value ]
else:
value = [ jsonify(value) ]
value = { 'values': value }
elif isinstance(value, list):
value = [ jsonify(v) for v in value ]
else:
value = jsonify(value)
# update and return
dict['value'] = value
return dict
# initialize state using default parameters
def initialize_state(trans, inputs, state, context=None):
context = ExpressionContext(state, context)
for input in inputs.itervalues():
state[input.name] = input.get_initial_value(trans, context)
# check the current state of a value and update it if necessary
def check_state(trans, input, default_value, context):
value = default_value
error = 'State validation failed.'
try:
# resolves the inconsistent definition of boolean parameters (see base.py) without modifying shared code
if input.type == 'boolean' and isinstance(default_value, basestring):
value, error = [util.string_as_bool(default_value), None]
else:
value, error = check_param(trans, input, default_value, context)
except Exception, err:
log.error('Checking parameter failed. %s', str(err))
pass
return [value, error]
# populates state with incoming url parameters
def populate_state(trans, inputs, state, errors, incoming, prefix="", context=None ):
context = ExpressionContext(state, context)
for input in inputs.itervalues():
key = prefix + input.name
if input.type == 'repeat':
group_state = state[input.name]
rep_index = 0
del group_state[:]
while True:
rep_name = "%s_%d" % (key, rep_index)
if not any([incoming_key.startswith(rep_name) for incoming_key in incoming.keys()]):
break
if rep_index < input.max:
new_state = {}
new_state['__index__'] = rep_index
initialize_state(trans, input.inputs, new_state, context)
group_state.append(new_state)
populate_state(trans, input.inputs, new_state, errors, incoming, prefix=rep_name + "|", context=context)
rep_index += 1
elif input.type == 'conditional':
group_state = state[input.name]
group_prefix = "%s|" % ( key )
test_param_key = group_prefix + input.test_param.name
default_value = incoming.get(test_param_key, group_state.get(input.test_param.name, None))
value, error = check_state(trans, input.test_param, default_value, context)
if error:
errors[test_param_key] = error
else:
try:
current_case = input.get_current_case(value, trans)
group_state = state[input.name] = {}
initialize_state(trans, input.cases[current_case].inputs, group_state, context)
populate_state( trans, input.cases[current_case].inputs, group_state, errors, incoming, prefix=group_prefix, context=context)
group_state['__current_case__'] = current_case
except Exception, e:
errors[test_param_key] = 'The selected case is unavailable/invalid.'
pass
group_state[input.test_param.name] = value
else:
default_value = incoming.get(key, state.get(input.name, None))
value, error = check_state(trans, input, default_value, context)
if error:
errors[key] = error
state[input.name] = value
# builds tool model including all attributes
def iterate(group_inputs, inputs, state_inputs, other_values=None):
other_values = ExpressionContext( state_inputs, other_values )
for input_index, input in enumerate( inputs.itervalues() ):
# create model dictionary
tool_dict = input.to_dict(trans)
if tool_dict is None:
continue
# state for subsection/group
group_state = state_inputs[input.name]
# iterate and update values
if input.type == 'repeat':
group_cache = tool_dict['cache'] = {}
for i in range( len( group_state ) ):
group_cache[i] = {}
iterate( group_cache[i], input.inputs, group_state[i], other_values )
elif input.type == 'conditional':
if 'test_param' in tool_dict:
test_param = tool_dict['test_param']
test_param['value'] = jsonify(group_state[test_param['name']])
if '__current_case__' in group_state:
i = group_state['__current_case__']
iterate(tool_dict['cases'][i]['inputs'], input.cases[i].inputs, group_state, other_values)
else:
# create input dictionary, try to pass other_values if to_dict function supports it e.g. dynamic options
try:
tool_dict = input.to_dict(trans, other_values=other_values)
except Exception:
pass
# identify name
input_name = tool_dict.get('name')
if input_name:
# update input value from tool state
if input_name in state_inputs:
tool_dict['value'] = state_inputs[input_name]
# sanitize if value exists
tool_dict = sanitize(tool_dict)
# backup final input dictionary
group_inputs[input_index] = tool_dict
# sanatization for the final tool state
def sanitize_state(state):
keys = None
if isinstance(state, dict):
keys = state
elif isinstance(state, list):
keys = range( len(state) )
if keys:
for k in keys:
if isinstance(state[k], dict) or isinstance(state[k], list):
sanitize_state(state[k])
else:
state[k] = jsonify(state[k])
# do param translation here, used by datasource tools
if self.input_translator:
self.input_translator.translate( params )
# initialize and populate tool state
state_inputs = {}
state_errors = {}
initialize_state(trans, self.inputs, state_inputs)
populate_state(trans, self.inputs, state_inputs, state_errors, params.__dict__)
# create basic tool model
tool_model = self.to_dict(trans)
tool_model['inputs'] = {}
# build tool model and tool state
iterate(tool_model['inputs'], self.inputs, state_inputs, '')
# sanitize tool state
sanitize_state(state_inputs)
# load tool help
tool_help = ''
if self.help:
tool_help = self.help
tool_help = tool_help.render( static_path=url_for( '/static' ), host_url=url_for('/', qualified=True) )
if type( tool_help ) is not unicode:
tool_help = unicode( tool_help, 'utf-8')
# check if citations exist
tool_citations = False
if self.citations:
tool_citations = True
# get tool versions
tool_versions = []
tools = self.app.toolbox.get_loaded_tools_by_lineage(self.id)
for t in tools:
tool_versions.append(t.version)
## add information with underlying requirements and their versions
tool_requirements = []
if self.requirements:
for requirement in self.requirements:
tool_requirements.append({
'name' : requirement.name,
'version' : requirement.version
})
# add additional properties
tool_model.update({
'help' : tool_help,
'citations' : tool_citations,
'biostar_url' : trans.app.config.biostar_url,
'message' : tool_message,
'versions' : tool_versions,
'requirements' : tool_requirements,
'errors' : state_errors,
'state_inputs' : state_inputs
})
# check for errors
if 'error' in tool_message:
return tool_message
# return enriched tool model
return tool_model
def _compare_tool_version( self, trans, job ):
"""
Compares a tool version with the tool version from a job (from ToolRunner).
"""
tool_id = job.tool_id
tool_version = job.tool_version
message = ''
try:
select_field, tools, tool = self.app.toolbox.get_tool_components( tool_id, tool_version=tool_version, get_loaded_tools_by_lineage=False, set_selected=True )
if tool is None:
trans.response.status = 500
return { 'error': 'This dataset was created by an obsolete tool (%s). Can\'t re-run.' % tool_id }
if ( self.id != tool_id and self.old_id != tool_id ) or self.version != tool_version:
if self.id == tool_id:
if tool_version == None:
# for some reason jobs don't always keep track of the tool version.
message = ''
else:
message = 'This job was initially run with tool version "%s", which is currently not available. ' % tool_version
if len( tools ) > 1:
message += 'You can re-run the job with the selected tool or choose another derivation of the tool.'
else:
message += 'You can re-run the job with this tool version, which is a derivation of the original tool.'
else:
if len( tools ) > 1:
message = 'This job was initially run with tool version "%s", which is currently not available. ' % tool_version
message += 'You can re-run the job with the selected tool or choose another derivation of the tool.'
else:
message = 'This job was initially run with tool id "%s", version "%s", which is ' % ( tool_id, tool_version )
message += 'currently not available. You can re-run the job with this tool, which is a derivation of the original tool.'
except Exception, error:
trans.response.status = 500
return { 'error': str (error) }
# can't rerun upload, external data sources, et cetera. workflow compatible will proxy this for now
#if not self.is_workflow_compatible:
# trans.response.status = 500
# return { 'error': 'The \'%s\' tool does currently not support re-running.' % self.name }
return message
def get_default_history_by_trans( self, trans, create=False ):
return trans.get_history( create=create )
+17 -22
View File
@@ -6,10 +6,7 @@ from galaxy import exceptions
from galaxy.managers import libraries, folders, roles
from galaxy.web import _future_expose_api as expose_api
from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous
from galaxy.model.orm import and_, not_, or_
from galaxy.web.base.controller import BaseAPIController
from sqlalchemy.orm.exc import MultipleResultsFound
from sqlalchemy.orm.exc import NoResultFound
import logging
log = logging.getLogger( __name__ )
@@ -48,7 +45,7 @@ class LibrariesController( BaseAPIController ):
def __decode_id( self, trans, encoded_id, object_name=None ):
"""
Try to decode the id.
Try to decode the id.
:param object_name: Name of the object the id belongs to. (optional)
:type object_name: str
@@ -149,7 +146,7 @@ class LibrariesController( BaseAPIController ):
synopsis = payload.get( 'synopsis', None )
else:
raise exceptions.RequestParameterMissingException( "You did not specify any payload." )
updated_library = self.library_manager.update( trans, library, name, description, synopsis )
updated_library = self.library_manager.update( trans, library, name, description, synopsis )
library_dict = self.library_manager.get_library_dict( trans, updated_library )
return library_dict
@@ -248,20 +245,20 @@ class LibrariesController( BaseAPIController ):
*POST /api/libraries/{encoded_library_id}/permissions
:param encoded_library_id: the encoded id of the library to set the permissions of
:type encoded_library_id: an encoded id string
:type encoded_library_id: an encoded id string
:param action: (required) describes what action should be performed
available actions: remove_restrictions, set_permissions
:type action: string
:type action: string
:param access_ids[]: list of Role.id defining roles that should have access permission on the library
:type access_ids[]: string or list
:type access_ids[]: string or list
:param add_ids[]: list of Role.id defining roles that should have add item permission on the library
:type add_ids[]: string or list
:type add_ids[]: string or list
:param manage_ids[]: list of Role.id defining roles that should have manage permission on the library
:type manage_ids[]: string or list
:type manage_ids[]: string or list
:param modify_ids[]: list of Role.id defining roles that should have modify permission on the library
:type modify_ids[]: string or list
:type modify_ids[]: string or list
:rtype: dictionary
:returns: dict of current roles for all available permission types
@@ -306,7 +303,7 @@ class LibrariesController( BaseAPIController ):
else:
invalid_access_roles_names.append( role_id )
if len( invalid_access_roles_names ) > 0:
log.warning( "The following roles could not be added to the library access permission: " + str( invalid_access_roles_names ) )
log.warning( "The following roles could not be added to the library access permission: " + str( invalid_access_roles_names ) )
# ADD TO LIBRARY ROLES
valid_add_roles = []
@@ -319,8 +316,8 @@ class LibrariesController( BaseAPIController ):
else:
invalid_add_roles_names.append( role_id )
if len( invalid_add_roles_names ) > 0:
log.warning( "The following roles could not be added to the add library item permission: " + str( invalid_add_roles_names ) )
log.warning( "The following roles could not be added to the add library item permission: " + str( invalid_add_roles_names ) )
# MANAGE LIBRARY ROLES
valid_manage_roles = []
invalid_manage_roles_names = []
@@ -332,8 +329,8 @@ class LibrariesController( BaseAPIController ):
else:
invalid_manage_roles_names.append( role_id )
if len( invalid_manage_roles_names ) > 0:
log.warning( "The following roles could not be added to the manage library permission: " + str( invalid_manage_roles_names ) )
log.warning( "The following roles could not be added to the manage library permission: " + str( invalid_manage_roles_names ) )
# MODIFY LIBRARY ROLES
valid_modify_roles = []
invalid_modify_roles_names = []
@@ -345,7 +342,7 @@ class LibrariesController( BaseAPIController ):
else:
invalid_modify_roles_names.append( role_id )
if len( invalid_modify_roles_names ) > 0:
log.warning( "The following roles could not be added to the modify library permission: " + str( invalid_modify_roles_names ) )
log.warning( "The following roles could not be added to the modify library permission: " + str( invalid_modify_roles_names ) )
permissions = { trans.app.security_agent.permitted_actions.LIBRARY_ACCESS : valid_access_roles }
permissions.update( { trans.app.security_agent.permitted_actions.LIBRARY_ADD : valid_add_roles } )
@@ -357,7 +354,7 @@ class LibrariesController( BaseAPIController ):
# Copy the permissions to the root folder
trans.app.security_agent.copy_library_permissions( trans, library, library.root_folder )
else:
raise exceptions.RequestParameterInvalidException( 'The mandatory parameter "action" has an invalid value.'
raise exceptions.RequestParameterInvalidException( 'The mandatory parameter "action" has an invalid value.'
'Allowed values are: "remove_restrictions", set_permissions"' )
roles = self.library_manager.get_current_roles( trans, library )
return roles
@@ -369,18 +366,16 @@ class LibrariesController( BaseAPIController ):
POST /api/libraries/{encoded_library_id}/permissions
Updates the library permissions.
"""
import galaxy.util
params = galaxy.util.Params( payload )
params = util.Params( payload )
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
role_params = params.get( k + '_in', [] )
in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( trans.security.decode_id( x ) ) for x in galaxy.util.listify( role_params ) ]
in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( trans.security.decode_id( x ) ) for x in util.listify( role_params ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_all_library_permissions( trans, library, permissions )
trans.sa_session.refresh( library )
# Copy the permissions to the root folder
trans.app.security_agent.copy_library_permissions( trans, library, library.root_folder )
message = "Permissions updated for library '%s'." % library.name
item = library.to_dict( view='element', value_mapper={ 'id' : trans.security.encode_id , 'root_folder_id' : trans.security.encode_id } )
return item
@@ -33,12 +33,12 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
.. note:: May be slow! Returns all content traversing recursively through all folders.
.. seealso:: :class:`galaxy.webapps.galaxy.api.FolderContentsController.index` for a non-recursive solution
:param library_id: encoded id string of the library
:type library_id: string
:param library_id: the encoded id of the library
:type library_id: str
:returns: list of dictionaries of the form:
* id: the encoded id of the library item
* name: the 'libary path'
* name: the 'library path'
or relationship of the library item to the root
* type: 'file' or 'folder'
* url: the url to get detailed information on the library item
@@ -113,7 +113,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
:param id: the encoded id of the library item to return
:type id: str
:param library_id: encoded id string of the library that contains this item
:param library_id: the encoded id of the library that contains this item
:type library_id: str
:returns: detailed library item information
@@ -123,7 +123,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
:func:`galaxy.model.LibraryDataset.to_dict` and
:attr:`galaxy.model.LibraryFolder.dict_element_visible_keys`
"""
class_name, content_id = self.__decode_library_content_id( trans, id )
class_name, content_id = self.__decode_library_content_id( id )
if class_name == 'LibraryFolder':
content = self.get_library_folder( trans, content_id, check_ownership=False, check_accessible=True )
rval = content.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id } )
@@ -150,27 +150,39 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
the HDA's encoded id in ``from_hda_id`` (and optionally ``ldda_message``).
:type library_id: str
:param library_id: encoded id string of the library where to create the new item
:param library_id: the encoded id of the library where to create the new item
:type payload: dict
:param payload: dictionary structure containing:
* folder_id: the parent folder of the new item
* folder_id: the encoded id of the parent folder of the new item
* create_type: the type of item to create ('file', 'folder' or 'collection')
* from_hda_id: (optional) the id of an accessible HDA to copy into the
library
* from_hda_id: (optional, only if create_type is 'file') the
encoded id of an accessible HDA to copy into the library
* ldda_message: (optional) the new message attribute of the LDDA created
* extended_metadata: (optional) dub-dictionary containing any extended
metadata to associate with the item
* link_data_only: (optional) either 'copy_files' (default) or 'link_to_files'
* upload_option: (optional) one of 'upload_file' (default), 'upload_directory' or 'upload_paths'
* server_dir: (optional) only if upload_option is 'upload_directory'
* filesystem_paths: (optional) only if upload_option is 'upload_paths' and the user is an admin
* server_dir: (optional, only if upload_option is
'upload_directory') relative path of the subdirectory of Galaxy
``library_import_dir`` to upload. All and only the files (i.e.
no subdirectories) contained in the specified directory will be
uploaded.
* filesystem_paths: (optional, only if upload_option is
'upload_paths' and the user is an admin) file paths on the
Galaxy server to upload to the library, one file per line
* link_data_only: (optional, only when upload_option is
'upload_directory' or 'upload_paths') either 'copy_files'
(default) or 'link_to_files'. Setting to 'link_to_files'
symlinks instead of copying the files
* name: (optional, only if create_type is 'folder') name of the
folder to create
* description: (optional, only if create_type is 'folder')
description of the folder to create
:rtype: dict
:returns: a dictionary containing the id, name,
and 'show' url of the new item
"""
create_type = None
if 'create_type' not in payload:
trans.response.status = 400
return "Missing required 'create_type' parameter."
@@ -182,10 +194,10 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
if 'folder_id' not in payload:
trans.response.status = 400
return "Missing requred 'folder_id' parameter."
return "Missing required 'folder_id' parameter."
else:
folder_id = payload.pop( 'folder_id' )
class_name, folder_id = self.__decode_library_content_id( trans, folder_id )
class_name, folder_id = self.__decode_library_content_id( folder_id )
try:
# security is checked in the downstream controller
parent = self.get_library_folder( trans, folder_id, check_ownership=False, check_accessible=False )
@@ -202,9 +214,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
#check for extended metadata, store it and pop it out of the param
#otherwise sanitize_param will have a fit
ex_meta_payload = None
if 'extended_metadata' in payload:
ex_meta_payload = payload.pop('extended_metadata')
ex_meta_payload = payload.pop('extended_metadata', None)
# Now create the desired content object, either file or folder.
if create_type == 'file':
@@ -276,7 +286,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
def _copy_hda_to_library_folder( self, trans, from_hda_id, library_id, folder_id, ldda_message='' ):
"""
Copies hda ``from_hda_id`` to library folder ``library_folder_id`` optionally
Copies hda ``from_hda_id`` to library folder ``folder_id``, optionally
adding ``ldda_message`` to the new ldda's ``message``.
``library_contents.create`` will branch to this if called with 'from_hda_id'
@@ -331,7 +341,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
:type id: str
:param id: the encoded id of the library item to return
:type library_id: str
:param library_id: encoded id string of the library that contains this item
:param library_id: the encoded id of the library that contains this item
:type payload: dict
:param payload: dictionary structure containing::
'converted_dataset_id':
@@ -350,7 +360,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
trans.sa_session.add( assoc )
trans.sa_session.flush()
def __decode_library_content_id( self, trans, content_id ):
def __decode_library_content_id( self, content_id ):
if len( content_id ) % 16 == 0:
return 'LibraryDataset', content_id
elif content_id.startswith( 'F' ):
+2 -307
View File
@@ -10,7 +10,7 @@ from galaxy.web.base.controller import UsesVisualizationMixin
from galaxy.visualization.genomes import GenomeRegion
from galaxy.util.json import dumps
from galaxy.visualization.data_providers.genome import *
from galaxy.tools.parameters import params_to_incoming, check_param
from galaxy.tools.parameters import check_param
from galaxy.tools.parameters import visit_input_values
from galaxy.tools.parameters.meta import expand_meta_parameters
from galaxy.managers.collections_util import dictify_dataset_collection_instance
@@ -85,7 +85,7 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
if not tool:
trans.response.status = 500
return { 'error': 'Could not find tool with id \'%s\'' % tool_id }
return self._build_dict(trans, tool, kwd)
return tool.to_json(trans, **kwd)
@_future_expose_api
@web.require_admin
@@ -493,308 +493,3 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
dataset_dict[ 'id' ] = trans.security.encode_id( dataset_dict[ 'id' ] )
dataset_dict[ 'track_config' ] = self.get_new_track_config( trans, output_dataset )
return dataset_dict
def _build_dict(self, trans, tool, kwd={}):
"""
Recursively creates a tool dictionary containing repeats, dynamic options and updated states.
"""
job_id = kwd.get('job_id', None)
dataset_id = kwd.get('dataset_id', None)
# load job details if provided
job = None
if job_id:
try:
job_id = trans.security.decode_id( job_id )
job = trans.sa_session.query( trans.app.model.Job ).get( job_id )
except Exception, exception:
trans.response.status = 500
return { 'error': 'Failed to retrieve job.' }
elif dataset_id:
try:
dataset_id = trans.security.decode_id( dataset_id )
data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id )
if not ( trans.user_is_admin() or trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), data.dataset ) ):
trans.response.status = 500
return { 'error': 'User has no access to dataset.' }
job = data.creating_job
if not job:
trans.response.status = 500
return { 'error': 'Creating job not found.' }
except Exception, exception:
trans.response.status = 500
return { 'error': 'Failed to get job information.' }
# load job parameters into incoming
tool_message = ''
if job:
try:
job_params = job.get_param_values( trans.app, ignore_errors = True )
job_messages = tool.check_and_update_param_values( job_params, trans, update_values=False )
tool_message = self._compare_tool_version(trans, tool, job)
params_to_incoming( kwd, tool.inputs, job_params, trans.app )
except Exception, exception:
trans.response.status = 500
return { 'error': str( exception ) }
# create parameter object
params = galaxy.util.Params( kwd, sanitize = False )
# convert value to jsonifiable value
def convert(v):
# check if value is numeric
isnumber = False
try:
float(v)
isnumber = True
except Exception:
pass
# fix hda parsing
if isinstance(v, HistoryDatasetAssociation):
return {
'id' : trans.security.encode_id(v.id),
'src' : 'hda'
}
elif isinstance(v, bool):
if v is True:
return 'true'
else:
return 'false'
elif isinstance(v, basestring) or isnumber:
return v
else:
return None
# ensures that input dictionary is jsonifiable
def sanitize(dict):
# get current value
value = dict['value'] if 'value' in dict else None
# identify lists
if dict['type'] in ['data']:
if isinstance(value, list):
value = [ convert(v) for v in value ]
else:
value = [ convert(value) ]
value = {
'batch' : dict['multiple'],
'values' : value
}
elif isinstance(value, list):
value = [ convert(v) for v in value ]
else:
value = convert(value)
# update and return
dict['value'] = value
return dict
# initialize state using default parameters
def initialize_state(trans, inputs, state, context=None):
context = ExpressionContext(state, context)
for input in inputs.itervalues():
state[input.name] = input.get_initial_value(trans, context)
# check the current state of a value and update it if necessary
def check_state(trans, input, default_value, context):
value = default_value
error = 'State validation failed.'
try:
# resolves the inconsistent definition of boolean parameters (see base.py) without modifying shared code
if input.type == 'boolean' and isinstance(default_value, basestring):
value, error = [util.string_as_bool(default_value), None]
else:
value, error = check_param(trans, input, default_value, context)
except Exception:
log.error('Checking parameter %s failed.', input.name)
pass
return [value, error]
# populates state with incoming url parameters
def populate_state(trans, inputs, state, errors, incoming, prefix="", context=None ):
context = ExpressionContext(state, context)
for input in inputs.itervalues():
key = prefix + input.name
if input.type == 'repeat':
group_state = state[input.name]
rep_index = 0
del group_state[:]
while True:
rep_name = "%s_%d" % (key, rep_index)
if not any([incoming_key.startswith(rep_name) for incoming_key in incoming.keys()]):
break
if rep_index < input.max:
new_state = {}
new_state['__index__'] = rep_index
initialize_state(trans, input.inputs, new_state, context)
group_state.append(new_state)
populate_state(trans, input.inputs, new_state, errors, incoming, prefix=rep_name + "|", context=context)
rep_index += 1
elif input.type == 'conditional':
group_state = state[input.name]
group_prefix = "%s|" % ( key )
test_param_key = group_prefix + input.test_param.name
default_value = incoming.get(test_param_key, group_state.get(input.test_param.name, None))
value, error = check_state(trans, input.test_param, default_value, context)
if error:
errors[test_param_key] = error
else:
current_case = input.get_current_case(value, trans)
group_state = state[input.name] = {}
initialize_state(trans, input.cases[current_case].inputs, group_state, context)
populate_state( trans, input.cases[current_case].inputs, group_state, errors, incoming, prefix=group_prefix, context=context)
group_state['__current_case__'] = current_case
group_state[input.test_param.name] = value
else:
default_value = incoming.get(key, state.get(input.name, None))
value, error = check_state(trans, input, default_value, context)
if error:
errors[key] = error
state[input.name] = value
# builds tool model including all attributes
def iterate(group_inputs, inputs, tool_state, other_values=None):
other_values = ExpressionContext( tool_state, other_values )
for input_index, input in enumerate( inputs.itervalues() ):
# create model dictionary
group_inputs[input_index] = input.to_dict(trans)
if group_inputs[input_index] is None:
continue
# identify stat for subsection/group
group_state = tool_state[input.name]
# iterate and update values
if input.type == 'repeat':
group_cache = group_inputs[input_index]['cache'] = {}
for i in range( len( group_state ) ):
group_cache[i] = {}
iterate( group_cache[i], input.inputs, group_state[i], other_values )
elif input.type == 'conditional':
try:
test_param = group_inputs[input_index]['test_param']
test_param['value'] = convert(group_state[test_param['name']])
except Exception:
pass
i = group_state['__current_case__']
iterate(group_inputs[input_index]['cases'][i]['inputs'], input.cases[i].inputs, group_state, other_values)
else:
# create input dictionary, try to pass other_values if to_dict function supports it e.g. dynamic options
try:
group_inputs[input_index] = input.to_dict(trans, other_values=other_values)
except Exception:
pass
# update input value from tool state
try:
group_inputs[input_index]['value'] = tool_state[group_inputs[input_index]['name']]
except Exception:
pass
# sanitize if value exists
if group_inputs[input_index]['value']:
group_inputs[input_index] = sanitize(group_inputs[input_index])
# do param translation here, used by datasource tools
if tool.input_translator:
tool.input_translator.translate( params )
# create tool state
state_inputs = {}
state_errors = {}
initialize_state(trans, tool.inputs, state_inputs)
populate_state(trans, tool.inputs, state_inputs, state_errors, params.__dict__)
# create basic tool model
tool_model = tool.to_dict(trans)
tool_model['inputs'] = {}
# build tool model
iterate(tool_model['inputs'], tool.inputs, state_inputs, '')
# load tool help
tool_help = ''
if tool.help:
tool_help = tool.help
tool_help = tool_help.render( static_path=web.url_for( '/static' ), host_url=web.url_for('/', qualified=True) )
if type( tool_help ) is not unicode:
tool_help = unicode( tool_help, 'utf-8')
# check if citations exist
tool_citations = False
if tool.citations:
tool_citations = True
# get tool versions
tool_versions = []
tools = self.app.toolbox.get_loaded_tools_by_lineage(tool.id)
for t in tools:
tool_versions.append(t.version)
## add information with underlying requirements and their versions
tool_requirements = []
if tool.requirements:
for requirement in tool.requirements:
tool_requirements.append({
'name' : requirement.name,
'version' : requirement.version
})
# add additional properties
tool_model.update({
'help' : tool_help,
'citations' : tool_citations,
'biostar_url' : trans.app.config.biostar_url,
'message' : tool_message,
'versions' : tool_versions,
'requirements' : tool_requirements,
'errors' : state_errors
})
# check for errors
if 'error' in tool_message:
return tool_message
# return enriched tool model
return tool_model
def _compare_tool_version( self, trans, tool, job ):
"""
Compares a tool version with the tool version from a job (from ToolRunner).
"""
tool_id = job.tool_id
tool_version = job.tool_version
message = ''
try:
select_field, tools, tool = self.app.toolbox.get_tool_components( tool_id, tool_version=tool_version, get_loaded_tools_by_lineage=False, set_selected=True )
if tool is None:
trans.response.status = 500
return { 'error': 'This dataset was created by an obsolete tool (%s). Can\'t re-run.' % tool_id }
if ( tool.id != tool_id and tool.old_id != tool_id ) or tool.version != tool_version:
if tool.id == tool_id:
if tool_version == None:
# for some reason jobs don't always keep track of the tool version.
message = ''
else:
message = 'This job was initially run with tool version "%s", which is currently not available. ' % tool_version
if len( tools ) > 1:
message += 'You can re-run the job with the selected tool or choose another derivation of the tool.'
else:
message += 'You can re-run the job with this tool version, which is a derivation of the original tool.'
else:
if len( tools ) > 1:
message = 'This job was initially run with tool version "%s", which is currently not available. ' % tool_version
message += 'You can re-run the job with the selected tool or choose another derivation of the tool.'
else:
message = 'This job was initially run with tool id "%s", version "%s", which is ' % ( tool_id, tool_version )
message += 'currently not available. You can re-run the job with this tool, which is a derivation of the original tool.'
except Exception, error:
trans.response.status = 500
return { 'error': str (error) }
# can't rerun upload, external data sources, et cetera. workflow compatible will proxy this for now
if not tool.is_workflow_compatible:
trans.response.status = 500
return { 'error': 'The \'%s\' tool does currently not support re-running.' % tool.name }
return message
@@ -4,6 +4,7 @@ import json
import os
import sgmllib
import urllib2
import copy
from sqlalchemy import and_
from tool_shed.util import common_util
@@ -609,16 +610,26 @@ class WorkflowController( BaseUIController, SharableMixin, UsesStoredWorkflowMix
is selected.
"""
tool_state = incoming.pop('tool_state', None)
trans.workflow_building_mode = True
module = module_factory.from_dict( trans, {
'type': type,
'tool_id': tool_id,
'tool_state': incoming.pop("tool_state")
'tool_state': tool_state
} )
# create tool model and default tool state (if missing)
tool_model = None
if not tool_state:
tool_model = module.tool.to_json(trans, **incoming)
module.state.inputs = copy.deepcopy(tool_model['state_inputs'])
# update module state
module.update_state( incoming )
if type == 'tool':
return {
'tool_model': tool_model,
'tool_state': module.get_state(),
'data_inputs': module.get_data_inputs(),
'data_outputs': module.get_data_outputs(),
@@ -653,6 +664,7 @@ class WorkflowController( BaseUIController, SharableMixin, UsesStoredWorkflowMix
'name': module.get_name(),
'tool_id': module.get_tool_id(),
'tool_state': module.get_state(),
'tool_model': module.tool.to_json(trans),
'tooltip': module.get_tooltip( static_path=url_for( '/static' ) ),
'data_inputs': module.get_data_inputs(),
'data_outputs': module.get_data_outputs(),
+4 -3
View File
@@ -18,7 +18,7 @@ from galaxy.dataset_collections import matching
from galaxy.web.framework import formbuilder
from galaxy.jobs.actions.post import ActionBox
from galaxy.model import PostJobAction
from galaxy.tools.parameters import check_param, DataToolParameter, DummyDataset, RuntimeValue, visit_input_values
from galaxy.tools.parameters import params_to_incoming, check_param, DataToolParameter, DummyDataset, RuntimeValue, visit_input_values
from galaxy.tools.parameters import DataCollectionToolParameter
from galaxy.tools.parameters.wrapped import make_dict_copy
from galaxy.tools.execute import execute
@@ -509,7 +509,8 @@ class ToolModule( WorkflowModule ):
message = "%s: using version '%s' instead of version '%s' indicated in this workflow." % ( tool_id, d.get( 'tool_version', 'Unspecified' ), module.get_tool_version() )
log.debug(message)
module.version_changes.append(message)
module.state.decode( d[ "tool_state" ], module.tool, module.trans.app, secure=secure )
if d[ "tool_state" ]:
module.state.decode( d[ "tool_state" ], module.tool, module.trans.app, secure=secure )
module.errors = d.get( "tool_errors", None )
module.post_job_actions = d.get( "post_job_actions", {} )
module.workflow_outputs = d.get( "workflow_outputs", [] )
@@ -687,7 +688,7 @@ class ToolModule( WorkflowModule ):
def get_config_form( self ):
self.add_dummy_datasets()
return self.trans.fill_template( "workflow/editor_tool_form.mako",
return self.trans.fill_template( "workflow/editor_tool_form.mako", module=self,
tool=self.tool, values=self.state.inputs, errors=( self.errors or {} ) )
def encode_runtime_state( self, trans, state ):
+2
View File
@@ -1,5 +1,7 @@
#!/bin/sh
./scripts/common_startup.sh
# A good place to look for nose info: http://somethingaboutorange.com/mrl/projects/nose/
rm -f run_functional_tests.log
+5 -1
View File
@@ -225,7 +225,11 @@ def main():
if not os.path.isabs( galaxy_test_file_dir ):
galaxy_test_file_dir = os.path.join( os.getcwd(), galaxy_test_file_dir )
library_import_dir = galaxy_test_file_dir
user_library_import_dir = os.path.join( galaxy_test_file_dir, 'users' )
import_dir = os.path.join( galaxy_test_file_dir, 'users' )
if os.path.exists(import_dir):
user_library_import_dir = import_dir
else:
user_library_import_dir = None
ignore_files = ()
start_server = 'GALAXY_TEST_EXTERNAL' not in os.environ
@@ -805,7 +805,7 @@ var Node = Backbone.Model.extend({
nodeView.addDataOutput( output );
} );
nodeView.render();
workflow.node_changed( this );
workflow.node_changed( this, true);
},
update_field_data : function( data ) {
var node = this;
@@ -1094,9 +1094,9 @@ $.extend( Workflow.prototype, {
this.active_node = node;
}
},
node_changed : function ( node ) {
node_changed : function ( node, force ) {
this.has_changes = true;
if ( this.active_node == node ) {
if ( this.active_node == node && (!parent.__NEWTOOLFORM__ || force)) {
// Reactive with new form_html
this.check_changes_in_active_form(); //Force changes to be saved even on new connection (previously dumped)
parent.show_form_for_tool( node.form_html + node.tooltip, node );
@@ -1305,6 +1305,12 @@ function add_node( type, title_text, tool_id ) {
return node;
}
function update_node( data ) {
var node = workflow.active_node;
if (node) {
node.update_field_data(data, true);
}
}
var ext_to_type = null;
var type_to_type = null;
+315
View File
@@ -0,0 +1,315 @@
/**
This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules.
*/
define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
'mvc/citation/citation-model', 'mvc/citation/citation-view',
'mvc/tools', 'mvc/tools/tools-template', 'mvc/tools/tools-content', 'mvc/tools/tools-section', 'mvc/tools/tools-tree'],
function(Utils, Deferred, Portlet, Ui, CitationModel, CitationView,
Tools, ToolTemplate, ToolContent, ToolSection, ToolTree) {
// create form view
return Backbone.View.extend({
// initialize
initialize: function(options) {
// log options
console.debug(options);
// link galaxy modal or create one
var galaxy = parent.Galaxy;
if (galaxy && galaxy.modal) {
this.modal = galaxy.modal;
} else {
this.modal = new Ui.Modal.View();
}
// check if the user is an admin
if (galaxy && galaxy.currUser) {
this.is_admin = galaxy.currUser.get('is_admin');
} else {
this.is_admin = false;
}
// link options
this.options = options;
// link container
this.container = this.options.container || 'body';
// create deferred processing queue handler
// this handler reduces the number of requests to the api by filtering redundant requests
this.deferred = new Deferred();
// set element
this.setElement('<div/>');
// add to main element
$(this.container).append(this.$el);
// build this form
this._buildForm();
},
/** Shows the final message (usually upon successful job submission)
*/
reciept: function($el) {
$(this.container).empty();
$(this.container).append($el);
},
/** Highlight and scroll to input element (currently only used for error notifications)
*/
highlight: function (input_id, message, silent) {
// get input field
var input_element = this.element_list[input_id];
// check input element
if (input_element) {
// mark error
input_element.error(message || 'Please verify this parameter.');
// scroll to first input element
if (!silent) {
$(this.container).animate({
scrollTop: input_element.$el.offset().top - 20
}, 500);
}
}
},
/** Main tool form build function. This function is called once a new model is available.
*/
_buildForm: function() {
// link this
var self = this;
// reset events
this.off('refresh');
this.off('reset');
// reset field list, which contains the input field elements
this.field_list = {};
// reset sequential input definition list, which contains the input definitions as provided from the api
this.input_list = {};
// reset input element list, which contains the dom elements of each input element (includes also the input field)
this.element_list = {};
// creates a tree/json data structure from the input form
this.tree = new ToolTree(this);
// request history content and build form
this.content = new ToolContent(this);
// link model options
var options = this.options;
// create ui elements
this._renderForm(options);
// rebuild the underlying data structure
this.tree.finalize();
// show errors
if (!this.workflow && options.errors) {
var error_messages = this.tree.matchResponse(options.errors);
for (var input_id in error_messages) {
this.highlight(input_id, error_messages[input_id], true);
}
}
// add refresh listener
this.on('refresh', function() {
// by using/reseting the deferred ajax queue the number of redundant calls is reduced
self.deferred.reset();
self.deferred.execute(function(){self._updateModel()});
});
// add reset listener
this.on('reset', function() {
for (var i in this.element_list) {
this.element_list[i].reset();
}
});
},
/** Renders the UI elements required for the form
*/
_renderForm: function(options) {
// link this
var self = this;
// create message view
this.message = new Ui.Message();
// button for version selection
var requirements_button = new Ui.ButtonIcon({
icon : 'fa-info-circle',
title : 'Requirements',
tooltip : 'Display tool requirements',
onclick : function() {
if (!this.visible) {
this.visible = true;
self.message.update({
persistent : true,
message : ToolTemplate.requirements(options),
status : 'info'
});
} else {
this.visible = false;
self.message.update({
message : ''
});
}
}
});
if (!options.requirements || options.requirements.length == 0) {
requirements_button.$el.hide();
}
// button for version selection
var versions_button = new Ui.ButtonMenu({
icon : 'fa-cubes',
title : 'Versions',
tooltip : 'Select another tool version'
});
if (options.versions && options.versions.length > 1) {
for (var i in options.versions) {
var version = options.versions[i];
if (version != options.version) {
versions_button.addMenu({
title : 'Switch to ' + version,
version : version,
icon : 'fa-cube',
onclick : function() {
// here we update the tool version (some tools encode the version also in the id)
options.id = options.id.replace(options.version, this.version);
options.version = this.version;
// rebuild the model and form
self.deferred.reset();
self.deferred.execute(function(){self._buildModel()});
}
});
}
}
} else {
versions_button.$el.hide();
}
// button menu
var menu_button = new Ui.ButtonMenu({
icon : 'fa-caret-down',
title : 'Options',
tooltip : 'View available options'
});
// configure button selection
if(options.biostar_url) {
// add question option
menu_button.addMenu({
icon : 'fa-question-circle',
title : 'Question?',
tooltip : 'Ask a question about this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/p/new/post/');
}
});
// create search button
menu_button.addMenu({
icon : 'fa-search',
title : 'Search',
tooltip : 'Search help for this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/t/' + options.id + '/');
}
});
};
// create share button
menu_button.addMenu({
icon : 'fa-share',
title : 'Share',
tooltip : 'Share this tool',
onclick : function() {
prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + galaxy_config.root + 'root?tool_id=' + options.id);
}
});
// add admin operations
if (this.is_admin) {
// create download button
menu_button.addMenu({
icon : 'fa-download',
title : 'Download',
tooltip : 'Download this tool',
onclick : function() {
window.location.href = galaxy_config.root + 'api/tools/' + options.id + '/download';
}
});
}
// create tool form section
this.section = new ToolSection.View(self, {
inputs : options.inputs,
cls : 'ui-table-plain'
});
// switch to classic tool form mako if the form definition is incompatible
if (this.incompatible) {
this.$el.hide();
$('#tool-form-classic').show();
return;
}
// create portlet
this.portlet = new Portlet.View({
icon : 'fa-wrench',
title : '<b>' + options.name + '</b> ' + options.description + ' (Galaxy Tool Version ' + options.version + ')',
cls : 'ui-portlet-slim',
operations: {
requirements : requirements_button,
menu : menu_button,
versions : versions_button
},
buttons : this.buttons
});
// append message
this.portlet.append(this.message.$el, true);
// append tool section
this.portlet.append(this.section.$el);
// start form
this.$el.empty();
this.$el.append(this.portlet.$el);
// append help
if (options.help != '') {
this.$el.append(ToolTemplate.help(options.help));
}
// append citations
if (options.citations) {
var $citations = $('<div/>');
var citations = new CitationModel.ToolCitationCollection();
citations.tool_id = options.id;
var citation_list_view = new CitationView.CitationListView({ el: $citations, collection: citations } );
citation_list_view.render();
citations.fetch();
this.$el.append($citations);
}
// show message if available in model
if (options.message) {
this.message.update({
persistent : true,
status : 'warning',
message : options.message
});
}
}
});
});
@@ -0,0 +1,66 @@
/**
This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules.
*/
define(['utils/utils', 'mvc/tools/tools-form-base'],
function(Utils, ToolFormBase) {
// create form view
var View = ToolFormBase.extend({
initialize: function(options) {
this.workflow = true;
ToolFormBase.prototype.initialize.call(this, options);
},
/** Builds a new model through api call and recreates the entire form
*/
_buildModel: function() {
},
/** Request a new model for an already created tool form and updates the form inputs
*/
_updateModel: function() {
// create the request dictionary
var self = this;
var current_state = this.tree.finalize();
// log tool state
console.debug('tools-form-workflow::_refreshForm() - Refreshing states.');
console.debug(current_state);
// register process
var process_id = this.deferred.register();
// build model url for request
var model_url = galaxy_config.root + 'workflow/editor_form_post?tool_id=' + this.options.id;
// post job
Utils.request({
type : 'GET',
url : model_url,
data : current_state,
success : function(node) {
parent.update_node(node);
// process completed
self.deferred.done(process_id);
// log success
console.debug('tools-form::_refreshForm() - States refreshed.');
console.debug(node);
},
error : function(response) {
// process completed
self.deferred.done(process_id);
// log error
console.debug('tools-form::_refreshForm() - Refresh request failed.');
console.debug(response);
}
});
}
});
return {
View: View
};
});
+17 -318
View File
@@ -1,79 +1,28 @@
/**
This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules.
*/
define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
'mvc/citation/citation-model', 'mvc/citation/citation-view',
'mvc/tools', 'mvc/tools/tools-template', 'mvc/tools/tools-content', 'mvc/tools/tools-section', 'mvc/tools/tools-tree', 'mvc/tools/tools-jobs'],
function(Utils, Deferred, Portlet, Ui, CitationModel, CitationView,
Tools, ToolTemplate, ToolContent, ToolSection, ToolTree, ToolJobs) {
define(['utils/utils', 'mvc/ui/ui-misc', 'mvc/tools/tools-form-base', 'mvc/tools/tools-jobs'],
function(Utils, Ui, ToolFormBase, ToolJobs) {
// create form view
var View = Backbone.View.extend({
// base element
container: 'body',
var View = ToolFormBase.extend({
// initialize
initialize: function(options) {
// log options
console.debug(options);
// link galaxy modal or create one
var galaxy = parent.Galaxy;
if (galaxy && galaxy.modal) {
this.modal = galaxy.modal;
} else {
this.modal = new Ui.Modal.View();
}
// check if the user is an admin
if (galaxy && galaxy.currUser) {
this.is_admin = galaxy.currUser.get('is_admin')
} else {
this.is_admin = false;
}
// link options
this.options = options;
// create deferred processing queue handler
// this handler reduces the number of requests to the api by filtering redundant requests
this.deferred = new Deferred();
// set element
this.setElement('<div/>');
// add to main element
$(this.container).append(this.$el);
// build this form
this._buildForm();
},
/** Shows the final message (usually upon successful job submission)
*/
reciept: function($el) {
$(this.container).empty();
$(this.container).append($el);
},
/** Highlight and scroll to input element (currently only used for error notifications)
*/
highlight: function (input_id, message, silent) {
// get input field
var input_element = this.element_list[input_id];
// check input element
if (input_element) {
// mark error
input_element.error(message || 'Please verify this parameter.');
// scroll to first input element
if (!silent) {
$(this.container).animate({
scrollTop: input_element.$el.offset().top - 20
}, 500);
}
var self = this;
this.job_handler = new ToolJobs(this);
this.buttons = {
execute : new Ui.Button({
icon : 'fa-check',
tooltip : 'Execute: ' + options.name,
title : 'Execute',
cls : 'btn btn-primary',
floating : 'clear',
onclick : function() {
self.job_handler.submit();
}
})
}
ToolFormBase.prototype.initialize.call(this, options);
},
/** Builds a new model through api call and recreates the entire form
@@ -250,256 +199,6 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
console.debug(response);
}
});
},
/** Main tool form build function. This function is called once a new model is available.
*/
_buildForm: function() {
// link this
var self = this;
// reset events
this.off('refresh');
this.off('reset');
// reset field list, which contains the input field elements
this.field_list = {};
// reset sequential input definition list, which contains the input definitions as provided from the api
this.input_list = {};
// reset input element list, which contains the dom elements of each input element (includes also the input field)
this.element_list = {};
// creates a tree/json data structure from the input form
this.tree = new ToolTree(this);
// creates the job handler
this.job_handler = new ToolJobs(this);
// request history content and build form
this.content = new ToolContent(this);
// link model options
var options = this.options;
// create ui elements
this._renderForm(options);
// rebuild the underlying data structure
this.tree.finalize();
// show errors
if (options.errors) {
var error_messages = this.tree.matchResponse(options.errors);
for (var input_id in error_messages) {
this.highlight(input_id, error_messages[input_id], true);
}
}
// add refresh listener
this.on('refresh', function() {
// by using/reseting the deferred ajax queue the number of redundant calls is reduced
self.deferred.reset();
self.deferred.execute(function(){self._updateModel()});
});
// add reset listener
this.on('reset', function() {
for (var i in this.element_list) {
this.element_list[i].reset();
}
});
},
/** Renders the UI elements required for the form
*/
_renderForm: function(options) {
// link this
var self = this;
// create message view
this.message = new Ui.Message();
// button for version selection
var requirements_button = new Ui.ButtonIcon({
icon : 'fa-info-circle',
title : 'Requirements',
tooltip : 'Display tool requirements',
onclick : function() {
if (!this.visible) {
this.visible = true;
self.message.update({
persistent : true,
message : ToolTemplate.requirements(options),
status : 'info'
});
} else {
this.visible = false;
self.message.update({
message : ''
});
}
}
});
if (!options.requirements || options.requirements.length == 0) {
requirements_button.$el.hide();
}
// button for version selection
var versions_button = new Ui.ButtonMenu({
icon : 'fa-cubes',
title : 'Versions',
tooltip : 'Select another tool version'
});
if (options.versions && options.versions.length > 1) {
for (var i in options.versions) {
var version = options.versions[i];
if (version != options.version) {
versions_button.addMenu({
title : 'Switch to ' + version,
version : version,
icon : 'fa-cube',
onclick : function() {
// here we update the tool version (some tools encode the version also in the id)
options.id = options.id.replace(options.version, this.version);
options.version = this.version;
// rebuild the model and form
self.deferred.reset();
self.deferred.execute(function(){self._buildModel()});
}
});
}
}
} else {
versions_button.$el.hide();
}
// button menu
var menu_button = new Ui.ButtonMenu({
icon : 'fa-caret-down',
title : 'Options',
tooltip : 'View available options'
});
// configure button selection
if(options.biostar_url) {
// add question option
menu_button.addMenu({
icon : 'fa-question-circle',
title : 'Question?',
tooltip : 'Ask a question about this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/p/new/post/');
}
});
// create search button
menu_button.addMenu({
icon : 'fa-search',
title : 'Search',
tooltip : 'Search help for this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/t/' + options.id + '/');
}
});
};
// create share button
menu_button.addMenu({
icon : 'fa-share',
title : 'Share',
tooltip : 'Share this tool',
onclick : function() {
prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + galaxy_config.root + 'root?tool_id=' + options.id);
}
});
// add admin operations
if (this.is_admin) {
// create download button
menu_button.addMenu({
icon : 'fa-download',
title : 'Download',
tooltip : 'Download this tool',
onclick : function() {
window.location.href = galaxy_config.root + 'api/tools/' + options.id + '/download';
}
});
}
// create tool form section
this.section = new ToolSection.View(self, {
inputs : options.inputs,
cls : 'ui-table-plain'
});
// switch to classic tool form mako if the form definition is incompatible
if (this.incompatible) {
this.$el.hide();
$('#tool-form-classic').show();
return;
}
// create portlet
this.portlet = new Portlet.View({
icon : 'fa-wrench',
title : '<b>' + options.name + '</b> ' + options.description + ' (Galaxy Tool Version ' + options.version + ')',
cls : 'ui-portlet-slim',
operations: {
requirements : requirements_button,
menu : menu_button,
versions : versions_button
},
buttons: {
execute : new Ui.Button({
icon : 'fa-check',
tooltip : 'Execute: ' + options.name,
title : 'Execute',
cls : 'btn btn-primary',
floating : 'clear',
onclick : function() {
self.job_handler.submit();
}
})
}
});
// append message
this.portlet.append(this.message.$el, true);
// append tool section
this.portlet.append(this.section.$el);
// start form
this.$el.empty();
this.$el.append(this.portlet.$el);
// append help
if (options.help != '') {
this.$el.append(ToolTemplate.help(options.help));
}
// append citations
if (options.citations) {
var $citations = $('<div/>');
var citations = new CitationModel.ToolCitationCollection();
citations.tool_id = options.id;
var citation_list_view = new CitationView.CitationListView({ el: $citations, collection: citations } );
citation_list_view.render();
citations.fetch();
this.$el.append($citations);
}
// show message if available in model
if (options.message) {
this.message.update({
persistent : true,
status : 'warning',
message : options.message
});
}
}
});
+1 -1
View File
@@ -98,7 +98,7 @@ return Backbone.Model.extend({
}
// validate non-optional fields
if (!input_def.optional && input_field.validate && !input_field.validate()) {
if (!input_def.optional && input_value == null) {
this.app.highlight(input_id);
return false;
}
+15 -1
View File
@@ -382,6 +382,11 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep
/** Data input field
*/
_fieldData : function(input_def) {
if (this.app.workflow) {
var extensions = Utils.textify(input_def.extensions.toString());
input_def.info = 'Data input \'' + input_def.name + '\' (' + extensions + ')';
return this._fieldHidden(input_def);
}
var self = this;
return new SelectContent.View(this.app, {
id : 'field-' + input_def.id,
@@ -398,6 +403,14 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep
/** Select/Checkbox/Radio options field
*/
_fieldSelect : function (input_def) {
// show text field in workflow
if (this.app.workflow && input_def.is_dynamic) {
if (!Utils.validate(input_def.value)) {
input_def.value = '';
}
return this._fieldText(input_def);
}
// configure options fields
var options = [];
for (var i in input_def.options) {
@@ -475,7 +488,8 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep
*/
_fieldHidden : function(input_def) {
return new Ui.Hidden({
id : 'field-' + input_def.id
id : 'field-' + input_def.id,
info : input_def.info
});
},
@@ -36,14 +36,7 @@ var View = Backbone.View.extend({
this.list = {};
// error messages
var extensions = options.extensions.toString();
if (extensions) {
extensions = extensions.replace(/,/g, ', ');
var pos = extensions.lastIndexOf(', ');
if (pos != -1) {
extensions = extensions.substr(0, pos) + ' or ' + extensions.substr(pos+1);
}
}
var extensions = Utils.textify(options.extensions);
var hda_error = 'No dataset available.';
if (extensions) {
hda_error = 'No ' + extensions + ' dataset available.';
+2 -2
View File
@@ -2,7 +2,7 @@
This class maps the tool form dom to an api compatible javascript dictionary.
*/
// dependencies
define([], function() {
define(['utils/utils'], function(Utils) {
// tool form tree
return Backbone.Model.extend({
@@ -120,7 +120,7 @@ return Backbone.Model.extend({
// handle default value
if (!field.skip) {
if (input.optional && field.validate && !field.validate()) {
if (field.validate && !field.validate(value)) {
value = null;
}
add (job_input_id, input.id, value);
+10 -4
View File
@@ -412,7 +412,7 @@ var Hidden = Backbone.View.extend({
initialize : function(options) {
// configure options
this.options = options;
// create new element
this.setElement(this._template(this.options));
@@ -425,14 +425,20 @@ var Hidden = Backbone.View.extend({
// value
value : function (new_val) {
if (new_val !== undefined) {
this.$el.val(new_val);
this.$('hidden').val(new_val);
}
return this.$el.val();
return this.$('hidden').val();
},
// element
_template: function(options) {
return '<hidden id="' + options.id + '" value="' + options.value + '"/>';
var tmpl = '<div id="' + options.id + '" >';
if (options.info) {
tmpl += '<label>' + options.info + '</label>';
}
tmpl += '<hidden value="' + options.value + '"/>' +
'</div>';
return tmpl;
}
});
+1 -10
View File
@@ -137,16 +137,7 @@ var Base = Backbone.View.extend({
/** Validate the selected option/options
*/
validate: function() {
var current = this.value();
if (!(current instanceof Array)) {
current = [current];
}
for (var i in current) {
if ([null, 'null', undefined].indexOf(current[i]) > -1) {
return false;
}
}
return true;
return Utils.validate(this.value());
},
/** Wait message during request processing
+1 -10
View File
@@ -96,16 +96,7 @@ var View = Backbone.View.extend({
/** Validate the current selection
*/
validate: function() {
var current = this.value();
if (!(current instanceof Array)) {
current = [current];
}
for (var i in current) {
if ([null, 'null', undefined].indexOf(current[i]) > -1) {
return false;
}
}
return true;
return Utils.validate(this.value());
},
/** Return the label/text of the current selection
+1 -1
View File
@@ -104,7 +104,7 @@ return Backbone.View.extend({
});
// define location
$('#left .unified-panel-header-inner').append((new UploadButton.View(this.ui_button)).$el);
$('.with-upload-button').append((new UploadButton.View(this.ui_button)).$el);
// load extension
var self = this;
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
define(["utils/utils","utils/deferred","mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/citation/citation-model","mvc/citation/citation-view","mvc/tools","mvc/tools/tools-template","mvc/tools/tools-content","mvc/tools/tools-section","mvc/tools/tools-tree"],function(g,h,f,k,i,a,d,c,e,j,b){return Backbone.View.extend({initialize:function(l){console.debug(l);var m=parent.Galaxy;if(m&&m.modal){this.modal=m.modal}else{this.modal=new k.Modal.View()}if(m&&m.currUser){this.is_admin=m.currUser.get("is_admin")}else{this.is_admin=false}this.options=l;this.container=this.options.container||"body";this.deferred=new h();this.setElement("<div/>");$(this.container).append(this.$el);this._buildForm()},reciept:function(l){$(this.container).empty();$(this.container).append(l)},highlight:function(m,n,l){var o=this.element_list[m];if(o){o.error(n||"Please verify this parameter.");if(!l){$(this.container).animate({scrollTop:o.$el.offset().top-20},500)}}},_buildForm:function(){var l=this;this.off("refresh");this.off("reset");this.field_list={};this.input_list={};this.element_list={};this.tree=new b(this);this.content=new e(this);var n=this.options;this._renderForm(n);this.tree.finalize();if(!this.workflow&&n.errors){var o=this.tree.matchResponse(n.errors);for(var m in o){this.highlight(m,o[m],true)}}this.on("refresh",function(){l.deferred.reset();l.deferred.execute(function(){l._updateModel()})});this.on("reset",function(){for(var p in this.element_list){this.element_list[p].reset()}})},_renderForm:function(u){var t=this;this.message=new k.Message();var q=new k.ButtonIcon({icon:"fa-info-circle",title:"Requirements",tooltip:"Display tool requirements",onclick:function(){if(!this.visible){this.visible=true;t.message.update({persistent:true,message:c.requirements(u),status:"info"})}else{this.visible=false;t.message.update({message:""})}}});if(!u.requirements||u.requirements.length==0){q.$el.hide()}var m=new k.ButtonMenu({icon:"fa-cubes",title:"Versions",tooltip:"Select another tool version"});if(u.versions&&u.versions.length>1){for(var o in u.versions){var r=u.versions[o];if(r!=u.version){m.addMenu({title:"Switch to "+r,version:r,icon:"fa-cube",onclick:function(){u.id=u.id.replace(u.version,this.version);u.version=this.version;t.deferred.reset();t.deferred.execute(function(){t._buildModel()})}})}}}else{m.$el.hide()}var p=new k.ButtonMenu({icon:"fa-caret-down",title:"Options",tooltip:"View available options"});if(u.biostar_url){p.addMenu({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(u.biostar_url+"/p/new/post/")}});p.addMenu({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(u.biostar_url+"/t/"+u.id+"/")}})}p.addMenu({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",window.location.origin+galaxy_config.root+"root?tool_id="+u.id)}});if(this.is_admin){p.addMenu({icon:"fa-download",title:"Download",tooltip:"Download this tool",onclick:function(){window.location.href=galaxy_config.root+"api/tools/"+u.id+"/download"}})}this.section=new j.View(t,{inputs:u.inputs,cls:"ui-table-plain"});if(this.incompatible){this.$el.hide();$("#tool-form-classic").show();return}this.portlet=new f.View({icon:"fa-wrench",title:"<b>"+u.name+"</b> "+u.description+" (Galaxy Tool Version "+u.version+")",cls:"ui-portlet-slim",operations:{requirements:q,menu:p,versions:m},buttons:this.buttons});this.portlet.append(this.message.$el,true);this.portlet.append(this.section.$el);this.$el.empty();this.$el.append(this.portlet.$el);if(u.help!=""){this.$el.append(c.help(u.help))}if(u.citations){var s=$("<div/>");var l=new i.ToolCitationCollection();l.tool_id=u.id;var n=new a.CitationListView({el:s,collection:l});n.render();l.fetch();this.$el.append(s)}if(u.message){this.message.update({persistent:true,status:"warning",message:u.message})}}})});
@@ -0,0 +1 @@
define(["utils/utils","mvc/tools/tools-form-base"],function(b,a){var c=a.extend({initialize:function(d){this.workflow=true;a.prototype.initialize.call(this,d)},_buildModel:function(){},_updateModel:function(){var d=this;var e=this.tree.finalize();console.debug("tools-form-workflow::_refreshForm() - Refreshing states.");console.debug(e);var g=this.deferred.register();var f=galaxy_config.root+"workflow/editor_form_post?tool_id="+this.options.id;b.request({type:"GET",url:f,data:e,success:function(h){parent.update_node(h);d.deferred.done(g);console.debug("tools-form::_refreshForm() - States refreshed.");console.debug(h)},error:function(h){d.deferred.done(g);console.debug("tools-form::_refreshForm() - Refresh request failed.");console.debug(h)}})}});return{View:c}});
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
define(["utils/utils","mvc/tools/tools-template"],function(b,a){return Backbone.Model.extend({initialize:function(c){this.app=c},submit:function(){var c=this;var d={tool_id:this.app.options.id,tool_version:this.app.options.version,inputs:this.app.tree.finalize()};this.app.trigger("reset");if(!this._validation(d)){console.debug("tools-jobs::submit - Submission canceled. Validation failed.");return}console.debug(d);this.app.modal.show({title:"Please wait...",body:"progress",closing_events:true,buttons:{Close:function(){c.app.modal.hide()}}});b.request({type:"POST",url:galaxy_config.root+"api/tools",data:d,success:function(e){c.app.modal.hide();c.app.reciept(a.success(e));c._refreshHdas()},error:function(e,g){c.app.modal.hide();if(e&&e.message&&e.message.data){var h=c.app.tree.matchResponse(e.message.data);for(var f in h){c.app.highlight(f,h[f]);break}}else{console.debug(e);c.app.modal.show({title:"Job submission failed",body:a.error(d),buttons:{Close:function(){c.app.modal.hide()}}})}}})},_validation:function(h){var d=h.inputs;var m=-1;var i=null;for(var k in d){var f=d[k];var l=this.app.tree.match(k);var e=this.app.field_list[l];var j=this.app.input_list[l];if(!l||!j||!e){console.debug("tools-jobs::_validation - Retrieving input objects failed.");continue}if(!j.optional&&e.validate&&!e.validate()){this.app.highlight(l);return false}if(f&&f.batch){var g=f.values.length;var c=null;if(g>0){c=f.values[0]&&f.values[0].src}if(c){if(i===null){i=c}else{if(i!==c){this.app.highlight(l,"Please select either dataset or dataset list fields for all batch mode fields.");return false}}}if(m===-1){m=g}else{if(m!==g){this.app.highlight(l,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>"+g+"</b> selection(s) while a previous field contains <b>"+m+"</b>.");return false}}}}return true},_refreshHdas:function(){if(parent.Galaxy&&parent.Galaxy.currHistoryPanel){parent.Galaxy.currHistoryPanel.refreshContents()}}})});
define(["utils/utils","mvc/tools/tools-template"],function(b,a){return Backbone.Model.extend({initialize:function(c){this.app=c},submit:function(){var c=this;var d={tool_id:this.app.options.id,tool_version:this.app.options.version,inputs:this.app.tree.finalize()};this.app.trigger("reset");if(!this._validation(d)){console.debug("tools-jobs::submit - Submission canceled. Validation failed.");return}console.debug(d);this.app.modal.show({title:"Please wait...",body:"progress",closing_events:true,buttons:{Close:function(){c.app.modal.hide()}}});b.request({type:"POST",url:galaxy_config.root+"api/tools",data:d,success:function(e){c.app.modal.hide();c.app.reciept(a.success(e));c._refreshHdas()},error:function(e,g){c.app.modal.hide();if(e&&e.message&&e.message.data){var h=c.app.tree.matchResponse(e.message.data);for(var f in h){c.app.highlight(f,h[f]);break}}else{console.debug(e);c.app.modal.show({title:"Job submission failed",body:a.error(d),buttons:{Close:function(){c.app.modal.hide()}}})}}})},_validation:function(h){var d=h.inputs;var m=-1;var i=null;for(var k in d){var f=d[k];var l=this.app.tree.match(k);var e=this.app.field_list[l];var j=this.app.input_list[l];if(!l||!j||!e){console.debug("tools-jobs::_validation - Retrieving input objects failed.");continue}if(!j.optional&&f==null){this.app.highlight(l);return false}if(f&&f.batch){var g=f.values.length;var c=null;if(g>0){c=f.values[0]&&f.values[0].src}if(c){if(i===null){i=c}else{if(i!==c){this.app.highlight(l,"Please select either dataset or dataset list fields for all batch mode fields.");return false}}}if(m===-1){m=g}else{if(m!==g){this.app.highlight(l,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>"+g+"</b> selection(s) while a previous field contains <b>"+m+"</b>.");return false}}}}return true},_refreshHdas:function(){if(parent.Galaxy&&parent.Galaxy.currHistoryPanel){parent.Galaxy.currHistoryPanel.refreshContents()}}})});
@@ -1 +1 @@
define(["utils/utils","mvc/ui/ui-table","mvc/ui/ui-misc","mvc/tools/tools-repeat","mvc/tools/tools-select-content","mvc/tools/tools-input"],function(d,b,g,c,a,e){var f=Backbone.View.extend({initialize:function(i,h){this.app=i;this.inputs=h.inputs;h.cls_tr="section-row";this.table=new b.View(h);this.setElement(this.table.$el);this.render()},render:function(){this.table.delAll();for(var h in this.inputs){this._add(this.inputs[h])}},_add:function(j){var i=this;var h=jQuery.extend(true,{},j);h.id=j.id=d.uuid();this.app.input_list[h.id]=h;var k=h.type;switch(k){case"conditional":this._addConditional(h);break;case"repeat":this._addRepeat(h);break;default:this._addRow(h)}},_addConditional:function(h){var j=this;h.test_param.id=h.id;var m=this._addRow(h.test_param);m.options.onchange=function(t){var p=j.app.tree.matchCase(h,t);for(var r in h.cases){var w=h.cases[r];var u=h.id+"-section-"+r;var o=j.table.get(u);var v=false;for(var q in w.inputs){var s=w.inputs[q].type;if(s&&s!=="hidden"){v=true;break}}if(r==p&&v){o.fadeIn("fast")}else{o.hide()}}j.app.trigger("refresh")};for(var l in h.cases){var k=h.id+"-section-"+l;var n=new f(this.app,{inputs:h.cases[l].inputs,cls:"ui-table-plain"});n.$el.addClass("ui-table-form-section");this.table.add(n.$el);this.table.append(k)}m.trigger("change")},_addRepeat:function(o){var r=this;var p=0;function m(i,t){var s=o.id+"-section-"+(p++);var u=null;if(t){u=function(){k.del(s);k.retitle(o.title);r.app.trigger("refresh")}}var v=new f(r.app,{inputs:i,cls:"ui-table-plain"});k.add({id:s,title:o.title,$el:v.$el,ondel:u});k.retitle(o.title)}var k=new c.View({title_new:o.title,max:o.max,onnew:function(){m(o.inputs,true);r.app.trigger("refresh")}});var h=o.min;var q=_.size(o.cache);for(var l=0;l<Math.max(q,h);l++){var n=null;if(l<q){n=o.cache[l]}else{n=o.inputs}m(n,l>=h)}var j=new e(this.app,{label:o.title,help:o.help,field:k});j.$el.addClass("ui-table-form-section");this.table.add(j.$el);this.table.append(o.id)},_addRow:function(h){var k=h.id;var i=this._createField(h);this.app.field_list[k]=i;var j=new e(this.app,{label:h.label,optional:h.optional,help:h.help,field:i});this.app.element_list[k]=j;this.table.add(j.$el);this.table.append(k);return i},_createField:function(h){var i=null;switch(h.type){case"text":i=this._fieldText(h);break;case"select":i=this._fieldSelect(h);break;case"data":i=this._fieldData(h);break;case"data_collection":i=this._fieldData(h);break;case"data_column":h.error_text="Missing columns in referenced dataset.";i=this._fieldSelect(h);break;case"hidden":i=this._fieldHidden(h);break;case"hidden_data":i=this._fieldHidden(h);break;case"integer":i=this._fieldSlider(h);break;case"float":i=this._fieldSlider(h);break;case"boolean":i=this._fieldBoolean(h);break;case"genomebuild":h.searchable=true;i=this._fieldSelect(h);break;case"drill_down":i=this._fieldDrilldown(h);break;case"baseurl":i=this._fieldHidden(h);break;default:this.app.incompatible=true;if(h.options){i=this._fieldSelect(h)}else{i=this._fieldText(h)}console.debug("tools-form::_addRow() : Auto matched field type ("+h.type+").")}if(h.value!==undefined){i.value(h.value)}return i},_fieldData:function(h){var i=this;return new a.View(this.app,{id:"field-"+h.id,extensions:h.extensions,multiple:h.multiple,type:h.type,data:h.options,onchange:function(){i.app.trigger("refresh")}})},_fieldSelect:function(h){var k=[];for(var l in h.options){var m=h.options[l];k.push({label:m[0],value:m[1]})}var n=g.Select;switch(h.display){case"checkboxes":n=g.Checkbox;break;case"radio":n=g.Radio;break}var j=this;return new n.View({id:"field-"+h.id,data:k,error_text:h.error_text||"No options available",multiple:h.multiple,searchable:h.searchable,onchange:function(){j.app.trigger("refresh")}})},_fieldDrilldown:function(h){var i=this;return new g.Drilldown.View({id:"field-"+h.id,data:h.options,display:h.display,onchange:function(){i.app.trigger("refresh")}})},_fieldText:function(h){var i=this;return new g.Input({id:"field-"+h.id,area:h.area,onchange:function(){i.app.trigger("refresh")}})},_fieldSlider:function(h){return new g.Slider.View({id:"field-"+h.id,precise:h.type=="float",min:h.min,max:h.max})},_fieldHidden:function(h){return new g.Hidden({id:"field-"+h.id})},_fieldBoolean:function(h){return new g.RadioButton.View({id:"field-"+h.id,data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]})}});return{View:f}});
define(["utils/utils","mvc/ui/ui-table","mvc/ui/ui-misc","mvc/tools/tools-repeat","mvc/tools/tools-select-content","mvc/tools/tools-input"],function(d,b,g,c,a,e){var f=Backbone.View.extend({initialize:function(i,h){this.app=i;this.inputs=h.inputs;h.cls_tr="section-row";this.table=new b.View(h);this.setElement(this.table.$el);this.render()},render:function(){this.table.delAll();for(var h in this.inputs){this._add(this.inputs[h])}},_add:function(j){var i=this;var h=jQuery.extend(true,{},j);h.id=j.id=d.uuid();this.app.input_list[h.id]=h;var k=h.type;switch(k){case"conditional":this._addConditional(h);break;case"repeat":this._addRepeat(h);break;default:this._addRow(h)}},_addConditional:function(h){var j=this;h.test_param.id=h.id;var m=this._addRow(h.test_param);m.options.onchange=function(t){var p=j.app.tree.matchCase(h,t);for(var r in h.cases){var w=h.cases[r];var u=h.id+"-section-"+r;var o=j.table.get(u);var v=false;for(var q in w.inputs){var s=w.inputs[q].type;if(s&&s!=="hidden"){v=true;break}}if(r==p&&v){o.fadeIn("fast")}else{o.hide()}}j.app.trigger("refresh")};for(var l in h.cases){var k=h.id+"-section-"+l;var n=new f(this.app,{inputs:h.cases[l].inputs,cls:"ui-table-plain"});n.$el.addClass("ui-table-form-section");this.table.add(n.$el);this.table.append(k)}m.trigger("change")},_addRepeat:function(o){var r=this;var p=0;function m(i,t){var s=o.id+"-section-"+(p++);var u=null;if(t){u=function(){k.del(s);k.retitle(o.title);r.app.trigger("refresh")}}var v=new f(r.app,{inputs:i,cls:"ui-table-plain"});k.add({id:s,title:o.title,$el:v.$el,ondel:u});k.retitle(o.title)}var k=new c.View({title_new:o.title,max:o.max,onnew:function(){m(o.inputs,true);r.app.trigger("refresh")}});var h=o.min;var q=_.size(o.cache);for(var l=0;l<Math.max(q,h);l++){var n=null;if(l<q){n=o.cache[l]}else{n=o.inputs}m(n,l>=h)}var j=new e(this.app,{label:o.title,help:o.help,field:k});j.$el.addClass("ui-table-form-section");this.table.add(j.$el);this.table.append(o.id)},_addRow:function(h){var k=h.id;var i=this._createField(h);this.app.field_list[k]=i;var j=new e(this.app,{label:h.label,optional:h.optional,help:h.help,field:i});this.app.element_list[k]=j;this.table.add(j.$el);this.table.append(k);return i},_createField:function(h){var i=null;switch(h.type){case"text":i=this._fieldText(h);break;case"select":i=this._fieldSelect(h);break;case"data":i=this._fieldData(h);break;case"data_collection":i=this._fieldData(h);break;case"data_column":h.error_text="Missing columns in referenced dataset.";i=this._fieldSelect(h);break;case"hidden":i=this._fieldHidden(h);break;case"hidden_data":i=this._fieldHidden(h);break;case"integer":i=this._fieldSlider(h);break;case"float":i=this._fieldSlider(h);break;case"boolean":i=this._fieldBoolean(h);break;case"genomebuild":h.searchable=true;i=this._fieldSelect(h);break;case"drill_down":i=this._fieldDrilldown(h);break;case"baseurl":i=this._fieldHidden(h);break;default:this.app.incompatible=true;if(h.options){i=this._fieldSelect(h)}else{i=this._fieldText(h)}console.debug("tools-form::_addRow() : Auto matched field type ("+h.type+").")}if(h.value!==undefined){i.value(h.value)}return i},_fieldData:function(h){if(this.app.workflow){var j=d.textify(h.extensions.toString());h.info="Data input '"+h.name+"' ("+j+")";return this._fieldHidden(h)}var i=this;return new a.View(this.app,{id:"field-"+h.id,extensions:h.extensions,multiple:h.multiple,type:h.type,data:h.options,onchange:function(){i.app.trigger("refresh")}})},_fieldSelect:function(h){if(this.app.workflow&&h.is_dynamic){if(!d.validate(h.value)){h.value=""}return this._fieldText(h)}var k=[];for(var l in h.options){var m=h.options[l];k.push({label:m[0],value:m[1]})}var n=g.Select;switch(h.display){case"checkboxes":n=g.Checkbox;break;case"radio":n=g.Radio;break}var j=this;return new n.View({id:"field-"+h.id,data:k,error_text:h.error_text||"No options available",multiple:h.multiple,searchable:h.searchable,onchange:function(){j.app.trigger("refresh")}})},_fieldDrilldown:function(h){var i=this;return new g.Drilldown.View({id:"field-"+h.id,data:h.options,display:h.display,onchange:function(){i.app.trigger("refresh")}})},_fieldText:function(h){var i=this;return new g.Input({id:"field-"+h.id,area:h.area,onchange:function(){i.app.trigger("refresh")}})},_fieldSlider:function(h){return new g.Slider.View({id:"field-"+h.id,precise:h.type=="float",min:h.min,max:h.max})},_fieldHidden:function(h){return new g.Hidden({id:"field-"+h.id,info:h.info})},_fieldBoolean:function(h){return new g.RadioButton.View({id:"field-"+h.id,data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]})}});return{View:f}});
@@ -1 +1 @@
define(["utils/utils","mvc/ui/ui-misc","mvc/ui/ui-tabs","mvc/tools/tools-template"],function(c,e,b,a){var d=Backbone.View.extend({initialize:function(f,o){this.app=f;this.options=o;var n=this;this.setElement("<div/>");this.list={};var m=[];if(o.type=="data_collection"){this.mode="collection"}else{if(o.multiple){this.mode="multiple"}else{this.mode="single"}}this.current=this.mode;this.list={};var j=o.extensions.toString();if(j){j=j.replace(/,/g,", ");var k=j.lastIndexOf(", ");if(k!=-1){j=j.substr(0,k)+" or "+j.substr(k+1)}}var h="No dataset available.";if(j){h="No "+j+" dataset available."}var l="No dataset list available.";if(j){l="No "+j+" dataset collection available."}if(this.mode=="single"){m.push({icon:"fa-file-o",label:"Single dataset",value:"single"});this.select_single=new e.Select.View({error_text:h,onchange:function(){n.trigger("change")}});this.list.single={field:this.select_single,type:"hda"}}if(this.mode=="single"||this.mode=="multiple"){m.push({icon:"fa-files-o",label:"Multiple datasets",value:"multiple"});this.select_multiple=new e.Select.View({multiple:true,error_text:h,onchange:function(){n.trigger("change")}});this.list.multiple={field:this.select_multiple,type:"hda"}}if(this.mode=="single"||this.mode=="collection"){m.push({icon:"fa-folder-o",label:"Dataset collection",value:"collection"});this.select_collection=new e.Select.View({error_text:l,onchange:function(){n.trigger("change")}});this.list.collection={field:this.select_collection,type:"hdca"}}this.button_type=new e.RadioButton.View({value:this.current,data:m,onchange:function(i){n.current=i;n.refresh();n.trigger("change")}});this.$batch=$(a.batchMode());if(_.size(this.list)>1){this.$el.append(c.wrap(this.button_type.$el))}for(var g in this.list){this.$el.append(this.list[g].field.$el)}this.$el.append(this.$batch);this.update(o.data);if(this.options.value!==undefined){this.value(this.options.value)}this.refresh();this.on("change",function(){if(o.onchange){o.onchange(n.value())}})},wait:function(){for(var f in this.list){this.list[f].field.wait()}},unwait:function(){for(var f in this.list){this.list[f].field.unwait()}},update:function(g){var l=[];for(var j in g.hda){var k=g.hda[j];l.push({label:k.hid+": "+k.name,value:k.id})}var f=[];for(var j in g.hdca){var h=g.hdca[j];f.push({label:h.hid+": "+h.name,value:h.id})}this.select_single&&this.select_single.update(l);this.select_multiple&&this.select_multiple.update(l);this.select_collection&&this.select_collection.update(f);this.app.content.add(g)},value:function(m){if(m&&m.values){try{var l=[];for(var h in m.values){l.push(m.values[h].id)}if(m&&m.values.length>0&&m.values[0].src=="hcda"){this.current="collection";this.select_collection.value(l[0])}else{if(this.mode=="multiple"){this.current="multiple";this.select_multiple.value(l)}else{this.current="single";this.select_single.value(l[0])}}this.refresh();var g=this._select();if(!g.validate()){g.value(g.first())}}catch(k){console.debug("tools-select-content::value() - Skipped.")}}var j=this._select().value();if(!(j instanceof Array)){j=[j]}var f={batch:this.mode=="single"&&this.current!="single",values:[]};for(var h in j){f.values.push({id:j[h],src:this.list[this.current].type})}return f},validate:function(){return this._select().validate()},refresh:function(){for(var g in this.list){var f=this.list[g].field.$el;if(this.current==g){f.show()}else{f.hide()}}if(this.mode=="single"&&this.current!="single"){this.$batch.show()}else{this.$batch.hide()}},_select:function(){return this.list[this.current].field}});return{View:d}});
define(["utils/utils","mvc/ui/ui-misc","mvc/ui/ui-tabs","mvc/tools/tools-template"],function(c,e,b,a){var d=Backbone.View.extend({initialize:function(l,g){this.app=l;this.options=g;var f=this;this.setElement("<div/>");this.list={};var j=[];if(g.type=="data_collection"){this.mode="collection"}else{if(g.multiple){this.mode="multiple"}else{this.mode="single"}}this.current=this.mode;this.list={};var k=c.textify(g.extensions);var n="No dataset available.";if(k){n="No "+k+" dataset available."}var m="No dataset list available.";if(k){m="No "+k+" dataset collection available."}if(this.mode=="single"){j.push({icon:"fa-file-o",label:"Single dataset",value:"single"});this.select_single=new e.Select.View({error_text:n,onchange:function(){f.trigger("change")}});this.list.single={field:this.select_single,type:"hda"}}if(this.mode=="single"||this.mode=="multiple"){j.push({icon:"fa-files-o",label:"Multiple datasets",value:"multiple"});this.select_multiple=new e.Select.View({multiple:true,error_text:n,onchange:function(){f.trigger("change")}});this.list.multiple={field:this.select_multiple,type:"hda"}}if(this.mode=="single"||this.mode=="collection"){j.push({icon:"fa-folder-o",label:"Dataset collection",value:"collection"});this.select_collection=new e.Select.View({error_text:m,onchange:function(){f.trigger("change")}});this.list.collection={field:this.select_collection,type:"hdca"}}this.button_type=new e.RadioButton.View({value:this.current,data:j,onchange:function(i){f.current=i;f.refresh();f.trigger("change")}});this.$batch=$(a.batchMode());if(_.size(this.list)>1){this.$el.append(c.wrap(this.button_type.$el))}for(var h in this.list){this.$el.append(this.list[h].field.$el)}this.$el.append(this.$batch);this.update(g.data);if(this.options.value!==undefined){this.value(this.options.value)}this.refresh();this.on("change",function(){if(g.onchange){g.onchange(f.value())}})},wait:function(){for(var f in this.list){this.list[f].field.wait()}},unwait:function(){for(var f in this.list){this.list[f].field.unwait()}},update:function(g){var l=[];for(var j in g.hda){var k=g.hda[j];l.push({label:k.hid+": "+k.name,value:k.id})}var f=[];for(var j in g.hdca){var h=g.hdca[j];f.push({label:h.hid+": "+h.name,value:h.id})}this.select_single&&this.select_single.update(l);this.select_multiple&&this.select_multiple.update(l);this.select_collection&&this.select_collection.update(f);this.app.content.add(g)},value:function(m){if(m&&m.values){try{var l=[];for(var h in m.values){l.push(m.values[h].id)}if(m&&m.values.length>0&&m.values[0].src=="hcda"){this.current="collection";this.select_collection.value(l[0])}else{if(this.mode=="multiple"){this.current="multiple";this.select_multiple.value(l)}else{this.current="single";this.select_single.value(l[0])}}this.refresh();var g=this._select();if(!g.validate()){g.value(g.first())}}catch(k){console.debug("tools-select-content::value() - Skipped.")}}var j=this._select().value();if(!(j instanceof Array)){j=[j]}var f={batch:this.mode=="single"&&this.current!="single",values:[]};for(var h in j){f.values.push({id:j[h],src:this.list[this.current].type})}return f},validate:function(){return this._select().validate()},refresh:function(){for(var g in this.list){var f=this.list[g].field.$el;if(this.current==g){f.show()}else{f.hide()}}if(this.mode=="single"&&this.current!="single"){this.$batch.show()}else{this.$batch.hide()}},_select:function(){return this.list[this.current].field}});return{View:d}});
@@ -1 +1 @@
define([],function(){return Backbone.Model.extend({initialize:function(a){this.app=a},finalize:function(f){var a=this;this.map_dict={};if(!this.app.section){return{}}f=f||{};var e={};var d={};this._iterate(this.app.section.$el,d);function c(i,h,g){e[i]=g;a.map_dict[i]=h}function b(n,q){for(var l in q){var j=q[l];if(j.input){var s=j.input;var m=n;if(n!=""){m+="|"}m+=s.name;switch(s.type){case"repeat":var h="section-";var v=[];var p=null;for(var u in j){var o=u.indexOf(h);if(o!=-1){o+=h.length;v.push(parseInt(u.substr(o)));if(!p){p=u.substr(0,o)}}}v.sort(function(w,i){return w-i});var l=0;for(var k in v){b(m+"_"+l++,j[p+v[k]])}break;case"conditional":var t=a.app.field_list[s.id].value();if(f[s.test_param.type]){t=f[s.test_param.type](t)}c(m+"|"+s.test_param.name,s.id,t);var g=a.matchCase(s,t);if(g!=-1){b(m,q[s.id+"-section-"+g])}break;default:var r=a.app.field_list[s.id];var t=r.value();if(f[s.type]){t=f[s.type](t)}if(!r.skip){if(s.optional&&r.validate&&!r.validate()){t=null}c(m,s.id,t)}}}}}b("",d);return e},match:function(a){return this.map_dict&&this.map_dict[a]},matchCase:function(a,c){if(a.test_param.type=="boolean"){if(c=="true"){c=a.test_param.truevalue||"true"}else{c=a.test_param.falsevalue||"false"}}for(var b in a.cases){if(a.cases[b].value==c){return b}}return -1},matchModel:function(c,e){var a={};var b=this;function d(f,o){for(var l in o){var h=o[l];var m=h.name;if(f!=""){m=f+"|"+m}switch(h.type){case"repeat":for(var k in h.cache){d(m+"_"+k,h.cache[k])}break;case"conditional":var p=h.test_param&&h.test_param.value;var g=b.matchCase(h,p);if(g!=-1){d(m,h.cases[g].inputs)}break;default:var n=b.map_dict[m];if(n){e(n,h)}}}}d("",c.inputs);return a},matchResponse:function(c){var a={};var b=this;function d(k,h){if(typeof h==="string"){var f=b.map_dict[k];if(f){a[f]=h}}else{for(var g in h){var e=g;if(k!==""){var j="|";if(h instanceof Array){j="_"}e=k+j+e}d(e,h[g])}}}d("",c);return a},_iterate:function(c,d){var a=this;var b=$(c).children();b.each(function(){var g=this;var f=$(g).attr("id");if($(g).hasClass("section-row")){d[f]={};var e=a.app.input_list[f];if(e){d[f]={input:e}}a._iterate(g,d[f])}else{a._iterate(g,d)}})}})});
define(["utils/utils"],function(a){return Backbone.Model.extend({initialize:function(b){this.app=b},finalize:function(g){var b=this;this.map_dict={};if(!this.app.section){return{}}g=g||{};var f={};var e={};this._iterate(this.app.section.$el,e);function d(j,i,h){f[j]=h;b.map_dict[j]=i}function c(o,r){for(var m in r){var k=r[m];if(k.input){var t=k.input;var n=o;if(o!=""){n+="|"}n+=t.name;switch(t.type){case"repeat":var j="section-";var w=[];var q=null;for(var v in k){var p=v.indexOf(j);if(p!=-1){p+=j.length;w.push(parseInt(v.substr(p)));if(!q){q=v.substr(0,p)}}}w.sort(function(x,i){return x-i});var m=0;for(var l in w){c(n+"_"+m++,k[q+w[l]])}break;case"conditional":var u=b.app.field_list[t.id].value();if(g[t.test_param.type]){u=g[t.test_param.type](u)}d(n+"|"+t.test_param.name,t.id,u);var h=b.matchCase(t,u);if(h!=-1){c(n,r[t.id+"-section-"+h])}break;default:var s=b.app.field_list[t.id];var u=s.value();if(g[t.type]){u=g[t.type](u)}if(!s.skip){if(s.validate&&!s.validate(u)){u=null}d(n,t.id,u)}}}}}c("",e);return f},match:function(b){return this.map_dict&&this.map_dict[b]},matchCase:function(b,d){if(b.test_param.type=="boolean"){if(d=="true"){d=b.test_param.truevalue||"true"}else{d=b.test_param.falsevalue||"false"}}for(var c in b.cases){if(b.cases[c].value==d){return c}}return -1},matchModel:function(d,f){var b={};var c=this;function e(g,p){for(var m in p){var k=p[m];var n=k.name;if(g!=""){n=g+"|"+n}switch(k.type){case"repeat":for(var l in k.cache){e(n+"_"+l,k.cache[l])}break;case"conditional":var q=k.test_param&&k.test_param.value;var h=c.matchCase(k,q);if(h!=-1){e(n,k.cases[h].inputs)}break;default:var o=c.map_dict[n];if(o){f(o,k)}}}}e("",d.inputs);return b},matchResponse:function(d){var b={};var c=this;function e(l,j){if(typeof j==="string"){var g=c.map_dict[l];if(g){b[g]=j}}else{for(var h in j){var f=h;if(l!==""){var k="|";if(j instanceof Array){k="_"}f=l+k+f}e(f,j[h])}}}e("",d);return b},_iterate:function(d,e){var b=this;var c=$(d).children();c.each(function(){var h=this;var g=$(h).attr("id");if($(h).hasClass("section-row")){e[g]={};var f=b.app.input_list[g];if(f){e[g]={input:f}}b._iterate(h,e[g])}else{b._iterate(h,e)}})}})});
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
define(["utils/utils"],function(b){var a=Backbone.View.extend({initialize:function(g){this.optionsDefault={visible:true,data:[],id:b.uuid(),error_text:"No data available.",wait_text:"Please wait..."};this.options=b.merge(g,this.optionsDefault);this.setElement("<div/>");this.$message=$("<div/>");this.$options=$(this._template(g));this.$el.append(this.$message);this.$el.append(this.$options);if(!this.options.visible){this.$el.hide()}this.update(this.options.data);if(this.options.value!==undefined){this.value(this.options.value)}var f=this;this.on("change",function(){f._change()})},update:function(g){var j=this._getValue();this.$options.empty();if(this._templateOptions){this.$options.append(this._templateOptions(g))}else{for(var h in g){var i=$(this._templateOption(g[h]));i.addClass("ui-option");this.$options.append(i)}}var f=this;this.$el.find("input").on("change",function(){f.value(f._getValue());f._change()});this._refresh();this.value(j)},value:function(g){if(g!==undefined){if(!(g instanceof Array)){g=[g]}this.$el.find("input").prop("checked",false);for(var f in g){this.$el.find('input[value="'+g[f]+'"]').first().prop("checked",true)}}return this._getValue()},exists:function(g){if(g!==undefined){if(!(g instanceof Array)){g=[g]}for(var f in g){if(this.$el.find('input[value="'+g[f]+'"]').length>0){return true}}}return false},first:function(){var f=this.$el.find("input");if(f.length>0){return f.val()}else{return undefined}},validate:function(){var g=this.value();if(!(g instanceof Array)){g=[g]}for(var f in g){if([null,"null",undefined].indexOf(g[f])>-1){return false}}return true},wait:function(){if(this._size()==0){this._messageShow(this.options.wait_text,"info");this.$options.hide()}},unwait:function(){this._messageHide();this._refresh()},_change:function(){if(this.options.onchange){this.options.onchange(this._getValue())}},_refresh:function(){if(this._size()==0){this._messageShow(this.options.error_text,"danger");this.$options.hide()}else{this._messageHide();this.$options.css("display","inline-block")}},_getValue:function(){var g=this.$el.find(":checked");if(g.length==0){return"null"}if(this.options.multiple){var f=[];g.each(function(){f.push($(this).val())});return f}else{return g.val()}},_size:function(){return this.$el.find(".ui-option").length},_messageShow:function(g,f){this.$message.show();this.$message.removeClass();this.$message.addClass("ui-message alert alert-"+f);this.$message.html(g)},_messageHide:function(){this.$message.hide()},_template:function(){return'<div class="ui-options"/>'}});var d={};d.View=a.extend({initialize:function(f){a.prototype.initialize.call(this,f)},_templateOption:function(f){return'<div><input type="radio" name="'+this.options.id+'" value="'+f.value+'"/>'+f.label+"<br></div>"}});var c={};c.View=a.extend({initialize:function(f){f.multiple=true;a.prototype.initialize.call(this,f)},_templateOption:function(f){return'<div><input type="checkbox" name="'+this.options.id+'" value="'+f.value+'"/>'+f.label+"<br></div>"}});var e={};e.View=a.extend({initialize:function(f){a.prototype.initialize.call(this,f)},value:function(f){if(f!==undefined){this.$el.find("input").prop("checked",false);this.$el.find("label").removeClass("active");this.$el.find('[value="'+f+'"]').prop("checked",true).closest("label").addClass("active")}return this._getValue()},_templateOption:function(g){var f='<label class="btn btn-default">';if(g.icon){f+='<i class="fa '+g.icon+'"/>'}f+='<input type="radio" name="'+this.options.id+'" value="'+g.value+'">'+g.label+"</label>";return f},_template:function(){return'<div class="btn-group ui-radiobutton" data-toggle="buttons"/>'}});return{Base:a,Radio:d,RadioButton:e,Checkbox:c}});
define(["utils/utils"],function(b){var a=Backbone.View.extend({initialize:function(g){this.optionsDefault={visible:true,data:[],id:b.uuid(),error_text:"No data available.",wait_text:"Please wait..."};this.options=b.merge(g,this.optionsDefault);this.setElement("<div/>");this.$message=$("<div/>");this.$options=$(this._template(g));this.$el.append(this.$message);this.$el.append(this.$options);if(!this.options.visible){this.$el.hide()}this.update(this.options.data);if(this.options.value!==undefined){this.value(this.options.value)}var f=this;this.on("change",function(){f._change()})},update:function(g){var j=this._getValue();this.$options.empty();if(this._templateOptions){this.$options.append(this._templateOptions(g))}else{for(var h in g){var i=$(this._templateOption(g[h]));i.addClass("ui-option");this.$options.append(i)}}var f=this;this.$el.find("input").on("change",function(){f.value(f._getValue());f._change()});this._refresh();this.value(j)},value:function(g){if(g!==undefined){if(!(g instanceof Array)){g=[g]}this.$el.find("input").prop("checked",false);for(var f in g){this.$el.find('input[value="'+g[f]+'"]').first().prop("checked",true)}}return this._getValue()},exists:function(g){if(g!==undefined){if(!(g instanceof Array)){g=[g]}for(var f in g){if(this.$el.find('input[value="'+g[f]+'"]').length>0){return true}}}return false},first:function(){var f=this.$el.find("input");if(f.length>0){return f.val()}else{return undefined}},validate:function(){return b.validate(this.value())},wait:function(){if(this._size()==0){this._messageShow(this.options.wait_text,"info");this.$options.hide()}},unwait:function(){this._messageHide();this._refresh()},_change:function(){if(this.options.onchange){this.options.onchange(this._getValue())}},_refresh:function(){if(this._size()==0){this._messageShow(this.options.error_text,"danger");this.$options.hide()}else{this._messageHide();this.$options.css("display","inline-block")}},_getValue:function(){var g=this.$el.find(":checked");if(g.length==0){return"null"}if(this.options.multiple){var f=[];g.each(function(){f.push($(this).val())});return f}else{return g.val()}},_size:function(){return this.$el.find(".ui-option").length},_messageShow:function(g,f){this.$message.show();this.$message.removeClass();this.$message.addClass("ui-message alert alert-"+f);this.$message.html(g)},_messageHide:function(){this.$message.hide()},_template:function(){return'<div class="ui-options"/>'}});var d={};d.View=a.extend({initialize:function(f){a.prototype.initialize.call(this,f)},_templateOption:function(f){return'<div><input type="radio" name="'+this.options.id+'" value="'+f.value+'"/>'+f.label+"<br></div>"}});var c={};c.View=a.extend({initialize:function(f){f.multiple=true;a.prototype.initialize.call(this,f)},_templateOption:function(f){return'<div><input type="checkbox" name="'+this.options.id+'" value="'+f.value+'"/>'+f.label+"<br></div>"}});var e={};e.View=a.extend({initialize:function(f){a.prototype.initialize.call(this,f)},value:function(f){if(f!==undefined){this.$el.find("input").prop("checked",false);this.$el.find("label").removeClass("active");this.$el.find('[value="'+f+'"]').prop("checked",true).closest("label").addClass("active")}return this._getValue()},_templateOption:function(g){var f='<label class="btn btn-default">';if(g.icon){f+='<i class="fa '+g.icon+'"/>'}f+='<input type="radio" name="'+this.options.id+'" value="'+g.value+'">'+g.label+"</label>";return f},_template:function(){return'<div class="btn-group ui-radiobutton" data-toggle="buttons"/>'}});return{Base:a,Radio:d,RadioButton:e,Checkbox:c}});
@@ -1 +1 @@
define(["utils/utils"],function(a){var b=Backbone.View.extend({optionsDefault:{id:"",cls:"",error_text:"No data available",visible:true,wait:false,multiple:false,searchable:false},initialize:function(d){this.options=a.merge(d,this.optionsDefault);this.setElement(this._template(this.options));this.$select=this.$el.find(".select");this.$icon=this.$el.find(".icon");this.$button=this.$el.find(".button");if(this.options.multiple){this.$select.prop("multiple",true);this.$select.addClass("ui-select-multiple");this.$icon.remove()}else{this.$el.addClass("ui-select")}this.update(this.options.data);if(this.options.value!==undefined){this.value(this.options.value)}if(!this.options.visible){this.hide()}if(this.options.wait){this.wait()}else{this.show()}var c=this;this.$select.on("change",function(){c._change()});this.on("change",function(){c._change()})},value:function(c){if(c!==undefined){this.$select.val(c);if(this.$select.select2){this.$select.select2("val",c)}}return this.$select.val()},first:function(){var c=this.$select.find("option");if(c.length>0){return c.val()}else{return undefined}},validate:function(){var d=this.value();if(!(d instanceof Array)){d=[d]}for(var c in d){if([null,"null",undefined].indexOf(d[c])>-1){return false}}return true},text:function(){return this.$select.find("option:selected").text()},show:function(){this.unwait();this.$select.show();this.$el.show()},hide:function(){this.$el.hide()},wait:function(){this.$icon.removeClass();this.$icon.addClass("fa fa-spinner fa-spin")},unwait:function(){this.$icon.removeClass();this.$icon.addClass("fa fa-caret-down")},disabled:function(){return this.$select.is(":disabled")},enable:function(){this.$select.prop("disabled",false)},disable:function(){this.$select.prop("disabled",true)},add:function(c){this.$select.append(this._templateOption(c));this._refresh()},del:function(c){this.$select.find("option[value="+c+"]").remove();this.$select.trigger("change");this._refresh()},update:function(c){var e=this.$select.val();this.$select.find("option").remove();for(var d in c){this.$select.append(this._templateOption(c[d]))}this._refresh();this.$select.val(e);if(!this.$select.val()){this.$select.val(this.first())}if(this.options.searchable){this.$button.hide();this.$select.select2("destroy");this.$select.select2()}},setOnChange:function(c){this.options.onchange=c},exists:function(c){return this.$select.find('option[value="'+c+'"]').length>0},_change:function(){if(this.options.onchange){this.options.onchange(this.$select.val())}},_refresh:function(){this.$select.find("option[value=null]").remove();var c=this.$select.find("option").length;if(c==0){this.disable();this.$select.append(this._templateOption({value:"null",label:this.options.error_text}))}else{this.enable()}},_templateOption:function(c){return'<option value="'+c.value+'">'+c.label+"</option>"},_template:function(c){return'<div id="'+c.id+'"><select id="select" class="select '+c.cls+" "+c.id+'"></select><div class="button"><i class="icon"/></div></div>'}});return{View:b}});
define(["utils/utils"],function(a){var b=Backbone.View.extend({optionsDefault:{id:"",cls:"",error_text:"No data available",visible:true,wait:false,multiple:false,searchable:false},initialize:function(d){this.options=a.merge(d,this.optionsDefault);this.setElement(this._template(this.options));this.$select=this.$el.find(".select");this.$icon=this.$el.find(".icon");this.$button=this.$el.find(".button");if(this.options.multiple){this.$select.prop("multiple",true);this.$select.addClass("ui-select-multiple");this.$icon.remove()}else{this.$el.addClass("ui-select")}this.update(this.options.data);if(this.options.value!==undefined){this.value(this.options.value)}if(!this.options.visible){this.hide()}if(this.options.wait){this.wait()}else{this.show()}var c=this;this.$select.on("change",function(){c._change()});this.on("change",function(){c._change()})},value:function(c){if(c!==undefined){this.$select.val(c);if(this.$select.select2){this.$select.select2("val",c)}}return this.$select.val()},first:function(){var c=this.$select.find("option");if(c.length>0){return c.val()}else{return undefined}},validate:function(){return a.validate(this.value())},text:function(){return this.$select.find("option:selected").text()},show:function(){this.unwait();this.$select.show();this.$el.show()},hide:function(){this.$el.hide()},wait:function(){this.$icon.removeClass();this.$icon.addClass("fa fa-spinner fa-spin")},unwait:function(){this.$icon.removeClass();this.$icon.addClass("fa fa-caret-down")},disabled:function(){return this.$select.is(":disabled")},enable:function(){this.$select.prop("disabled",false)},disable:function(){this.$select.prop("disabled",true)},add:function(c){this.$select.append(this._templateOption(c));this._refresh()},del:function(c){this.$select.find("option[value="+c+"]").remove();this.$select.trigger("change");this._refresh()},update:function(c){var e=this.$select.val();this.$select.find("option").remove();for(var d in c){this.$select.append(this._templateOption(c[d]))}this._refresh();this.$select.val(e);if(!this.$select.val()){this.$select.val(this.first())}if(this.options.searchable){this.$button.hide();this.$select.select2("destroy");this.$select.select2()}},setOnChange:function(c){this.options.onchange=c},exists:function(c){return this.$select.find('option[value="'+c+'"]').length>0},_change:function(){if(this.options.onchange){this.options.onchange(this.$select.val())}},_refresh:function(){this.$select.find("option[value=null]").remove();var c=this.$select.find("option").length;if(c==0){this.disable();this.$select.append(this._templateOption({value:"null",label:this.options.error_text}))}else{this.enable()}},_templateOption:function(c){return'<option value="'+c.value+'">'+c.label+"</option>"},_template:function(c){return'<div id="'+c.id+'"><select id="select" class="select '+c.cls+" "+c.id+'"></select><div class="button"><i class="icon"/></div></div>'}});return{View:b}});
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
define(["libs/underscore"],function(k){function d(l){return $("<div/>").text(l).html()}function e(l){top.__utils__get__=top.__utils__get__||{};if(l.cache&&top.__utils__get__[l.url]){l.success&&l.success(top.__utils__get__[l.url]);console.debug("utils.js::get() - Fetching from cache ["+l.url+"].")}else{h({url:l.url,data:l.data,success:function(m){top.__utils__get__[l.url]=m;l.success&&l.success(m)},error:function(m){l.error&&l.error(m)}})}}function h(m){var l={contentType:"application/json",type:m.type||"GET",data:m.data||{},url:m.url};if(l.type=="GET"||l.type=="DELETE"){if(l.url.indexOf("?")==-1){l.url+="?"}else{l.url+="&"}l.url=l.url+$.param(l.data);l.data=null}else{l.dataType="json";l.url=l.url;l.data=JSON.stringify(l.data)}$.ajax(l).done(function(n){if(typeof n==="string"){try{n=n.replace("Infinity,",'"Infinity",');n=jQuery.parseJSON(n)}catch(o){console.debug(o)}}m.success&&m.success(n)}).fail(function(o){var n=null;try{n=jQuery.parseJSON(o.responseText)}catch(p){n=o.responseText}m.error&&m.error(n,o)})}function i(o,l){var m=$('<div class="'+o+'"></div>');m.appendTo(":eq(0)");var n=m.css(l);m.remove();return n}function g(l){if(!$('link[href^="'+l+'"]').length){$('<link href="'+galaxy_config.root+l+'" rel="stylesheet">').appendTo("head")}}function j(l,m){if(l){return k.defaults(l,m)}else{return m}}function b(m,o){var n="";if(m>=100000000000){m=m/100000000000;n="TB"}else{if(m>=100000000){m=m/100000000;n="GB"}else{if(m>=100000){m=m/100000;n="MB"}else{if(m>=100){m=m/100;n="KB"}else{if(m>0){m=m*10;n="b"}else{return"<strong>-</strong>"}}}}}var l=(Math.round(m)/10);if(o){return l+" "+n}else{return"<strong>"+l+"</strong> "+n}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(l){var m=$("<p></p>");m.append(l);return m}function f(){var n=new Date();var l=(n.getHours()<10?"0":"")+n.getHours();var m=(n.getMinutes()<10?"0":"")+n.getMinutes();var o=n.getDate()+"/"+(n.getMonth()+1)+"/"+n.getFullYear()+", "+l+":"+m;return o}return{cssLoadFile:g,cssGetAttribute:i,get:e,merge:j,bytesToString:b,uuid:a,time:f,wrap:c,request:h,sanitize:d}});
define(["libs/underscore"],function(m){function d(n){return $("<div/>").text(n).html()}function l(o){if(!(o instanceof Array)){o=[o]}for(var n in o){if(["None",null,"null",undefined,"undefined"].indexOf(o[n])>-1){return false}}return true}function h(n){var n=n.toString();if(n){n=n.replace(/,/g,", ");var o=n.lastIndexOf(", ");if(o!=-1){n=n.substr(0,o)+" or "+n.substr(o+1)}return n}return""}function e(n){top.__utils__get__=top.__utils__get__||{};if(n.cache&&top.__utils__get__[n.url]){n.success&&n.success(top.__utils__get__[n.url]);console.debug("utils.js::get() - Fetching from cache ["+n.url+"].")}else{i({url:n.url,data:n.data,success:function(o){top.__utils__get__[n.url]=o;n.success&&n.success(o)},error:function(o){n.error&&n.error(o)}})}}function i(o){var n={contentType:"application/json",type:o.type||"GET",data:o.data||{},url:o.url};if(n.type=="GET"||n.type=="DELETE"){if(n.url.indexOf("?")==-1){n.url+="?"}else{n.url+="&"}n.url=n.url+$.param(n.data);n.data=null}else{n.dataType="json";n.url=n.url;n.data=JSON.stringify(n.data)}$.ajax(n).done(function(p){if(typeof p==="string"){try{p=p.replace("Infinity,",'"Infinity",');p=jQuery.parseJSON(p)}catch(q){console.debug(q)}}o.success&&o.success(p)}).fail(function(q){var p=null;try{p=jQuery.parseJSON(q.responseText)}catch(r){p=q.responseText}o.error&&o.error(p,q)})}function j(q,n){var o=$('<div class="'+q+'"></div>');o.appendTo(":eq(0)");var p=o.css(n);o.remove();return p}function g(n){if(!$('link[href^="'+n+'"]').length){$('<link href="'+galaxy_config.root+n+'" rel="stylesheet">').appendTo("head")}}function k(n,o){if(n){return m.defaults(n,o)}else{return o}}function b(o,q){var p="";if(o>=100000000000){o=o/100000000000;p="TB"}else{if(o>=100000000){o=o/100000000;p="GB"}else{if(o>=100000){o=o/100000;p="MB"}else{if(o>=100){o=o/100;p="KB"}else{if(o>0){o=o*10;p="b"}else{return"<strong>-</strong>"}}}}}var n=(Math.round(o)/10);if(q){return n+" "+p}else{return"<strong>"+n+"</strong> "+p}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(n){var o=$("<p></p>");o.append(n);return o}function f(){var p=new Date();var n=(p.getHours()<10?"0":"")+p.getHours();var o=(p.getMinutes()<10?"0":"")+p.getMinutes();var q=p.getDate()+"/"+(p.getMonth()+1)+"/"+p.getFullYear()+", "+n+":"+o;return q}return{cssLoadFile:g,cssGetAttribute:j,get:e,merge:k,bytesToString:b,uuid:a,time:f,wrap:c,request:i,sanitize:d,textify:h,validate:l}});
+37 -1
View File
@@ -14,6 +14,40 @@ function sanitize(content) {
return $('<div/>').text(content).html();
};
/**
* Validate atomic values or list of values
* usually used for selectable options
* @param{String} value - Value or list to be validated
*/
function validate (value) {
if (!(value instanceof Array)) {
value = [value];
}
for (var i in value) {
if (['None', null, 'null', undefined, 'undefined'].indexOf(value[i]) > -1) {
return false;
}
}
return true;
};
/**
* Convert list to pretty string
* @param{String} lst - List of strings to be converted in human readable list sentence
*/
function textify(lst) {
var lst = lst.toString();
if (lst) {
lst = lst.replace(/,/g, ', ');
var pos = lst.lastIndexOf(', ');
if (pos != -1) {
lst = lst.substr(0, pos) + ' or ' + lst.substr(pos+1);
}
return lst;
}
return '';
};
/**
* Request handler for GET
* @param{String} url - Url request is made to
@@ -212,7 +246,9 @@ return {
time: time,
wrap: wrap,
request: request,
sanitize: sanitize
sanitize: sanitize,
textify: textify,
validate: validate
};
});
+1 -1
View File
@@ -1742,7 +1742,7 @@ div.unified-panel-body{position:absolute;top:30px;bottom:0;width:100%}
#masthead li.dropdown>a:hover .caret{border-top-color:gold;border-bottom-color:gold}
#masthead .navbar-brand{position:absolute;left:0;top:0;font-family:verdana;font-weight:bold;font-size:20px;line-height:1;color:white;padding:5px 20px 12px;margin-left:-15px;z-index:2000}#masthead .navbar-brand img{display:inline;width:26px;vertical-align:top}
#masthead .navbar-brand a{color:white;text-decoration:none}
#masthead .iconbar{position:absolute;top:6px;right:110px;cursor:pointer;color:#999;overflow:hidden}#masthead .iconbar .symbol{float:left;margin:0px 10px;height:26px;font-size:11px}
#masthead .iconbar{position:absolute;top:7px;right:110px;cursor:pointer;color:#999;overflow:hidden}#masthead .iconbar .symbol{float:left;margin:0px 10px;height:26px;font-size:11px}
#masthead .iconbar .symbol .number{font-weight:bold;font-size:10px;font-family:"Lucida Grande",verdana,arial,helvetica,sans-serif;position:relative;left:23px;top:-12px}
#masthead .iconbar .toggle{color:gold}
.quota-meter-container{position:absolute;top:0;right:0;height:32px}
+1 -1
View File
@@ -425,7 +425,7 @@ div.unified-panel-body {
.iconbar
{
position : absolute;
top : 6px;
top : 7px;
right : 110px;
cursor : pointer;
color : @gray-light;
+5 -5
View File
@@ -32,7 +32,7 @@
{
form.elements[i].disabled = true;
}
}
var hiddenInput = document.createElement('input');
hiddenInput.type = 'hidden';
@@ -52,7 +52,7 @@
<p><b>Dataset ${hda.hid}: ${hda.display_name() | h}</b></p>
<% job = hda.creating_job %>
%if job:
%if job.traceback:
The Galaxy framework encountered the following error while attempting to run the tool:
<pre>${ util.unicodify( job.traceback ) | h}</pre>
@@ -82,8 +82,8 @@
%>
<h2>Report this error to the Galaxy Team</h2>
<p>
The Galaxy team regularly reviews errors that occur in the application.
However, if you would like to provide additional information (such as
The Galaxy team regularly reviews errors that occur in the application.
However, if you would like to provide additional information (such as
what you were trying to do when the error occurred) and a contact e-mail
address, we will be better able to investigate your problem and get back
to you.
@@ -99,7 +99,7 @@
</div>
<div class="form-row">
<label>Message</label>
<textarea name="message" rows="10" cols="40"></textarea>
<textarea name="message" rows="10" cols="60"></textarea>
</div>
<div class="form-row">
<input type="submit" name="submit_error_report" value="Report" onclick="return sendReport( this, this.form, '_self' );"/>
+1 -1
View File
@@ -58,7 +58,7 @@
<%def name="left_panel()">
<div class="unified-panel-header" unselectable="on">
<div class='unified-panel-header-inner'>
<div class='unified-panel-header-inner with-upload-button'>
${n_('Tools')}
</div>
</div>
+1 -3
View File
@@ -8,12 +8,10 @@ ${h.css('base', 'jquery-ui/smoothness/jquery-ui')}
<%
## TEMPORARY: create tool dictionary in mako while both tool forms are in use.
## This avoids making two separate requests since the classic form requires the mako anyway.
from galaxy.webapps.galaxy.api.tools import ToolsController
controller = ToolsController(trans.app)
params = dict(trans.request.params)
if 'id' in params:
params['dataset_id'] = params['id']
self.form_config = controller._build_dict(trans, tool, params)
self.form_config = tool.to_json(trans, **params)
self.form_config.update({
'id' : tool.id,
'job_id' : trans.security.encode_id( job.id ) if job else None,
@@ -1,127 +1,161 @@
<%
from galaxy.tools.parameters import DataToolParameter, RuntimeValue
from galaxy.tools.parameters import DataCollectionToolParameter
from galaxy.util.expressions import ExpressionContext
%>
## TEMPORARY SWITCH FOR THE NEW TOOL FORM
%if False:
${h.js("libs/bibtex", "libs/jquery/jquery-ui")}
${h.css('jquery-ui/smoothness/jquery-ui')}
<%
## TEMPORARY: create tool dictionary in mako while both tool forms are in use.
## This avoids making two separate requests since the classic form requires the mako anyway.
from galaxy.tools.parameters import params_to_incoming
incoming = {}
params_to_incoming( incoming, tool.inputs, module.state.inputs, trans.app )
self.form_config = tool.to_json(trans, **incoming)
self.form_config.update({
'id' : tool.id,
'job_id' : trans.security.encode_id( job.id ) if job else None,
'history_id' : trans.security.encode_id( trans.history.id ),
'workflow' : True,
'container' : '#right-content'
})
%>
<script>
parent.__NEWTOOLFORM__ = true;
require(['mvc/tools/tools-form-workflow'], function(ToolsForm){
$(function(){
var form = new ToolsForm.View(${ h.dumps(self.form_config) });
});
});
</script>
<form method="post" action="${h.url_for(controller='workflow', action='editor_form_post' )}">
<div class="toolForm">
<input type="hidden" name="tool_id" value="${tool.id}" />
</div>
</form>
%else:
<%
from galaxy.tools.parameters import DataToolParameter, RuntimeValue
from galaxy.tools.parameters import DataCollectionToolParameter
from galaxy.util.expressions import ExpressionContext
%>
<%def name="do_inputs( inputs, values, errors, prefix, ctx=None )">
<% ctx = ExpressionContext( values, ctx ) %>
%for input_index, input in enumerate( inputs.itervalues() ):
%if input.type == "repeat":
<div class="repeat-group form-row">
<label>${input.title_plural}:</label>
<% repeat_values = values[input.name] %>
%for i in range( len( repeat_values ) ):
<%
if input.name in errors:
rep_errors = errors[input.name][i]
else:
rep_errors = dict()
index = repeat_values[i]['__index__']
%>
<div class="repeat-group-item">
<div class="form-title-row"><label>${input.title} ${i + 1}</label></div>
${do_inputs( input.inputs, repeat_values[ i ], rep_errors, prefix + input.name + "_" + str(index) + "|", ctx )}
<div class="form-row"><input type="submit" name="${prefix}${input.name}_${index}_remove" value="Remove ${input.title} ${i+1}"></div>
</div>
%endfor
<div class="form-row"><input type="submit" name="${prefix}${input.name}_add" value="Add new ${input.title}"></div>
</div>
%elif input.type == "conditional":
%if input.is_job_resource_conditional:
<% continue %>
%endif
<% group_values = values[input.name] %>
<% current_case = group_values['__current_case__'] %>
<% group_prefix = prefix + input.name + "|" %>
<% group_errors = errors.get( input.name, {} ) %>
${row_for_param( input.test_param, group_values[ input.test_param.name ], group_errors, group_prefix, ctx, allow_runtime=False )}
${do_inputs( input.cases[ current_case ].inputs, group_values, group_errors, group_prefix, ctx )}
%else:
%if input.name in values:
${row_for_param( input, values[ input.name ], errors, prefix, ctx )}
%else:
<% errors[ input.name ] = 'Value not stored, displaying default' %>
${row_for_param( input, input.get_initial_value( trans, values ), errors, prefix, ctx )}
%endif
%endif
%endfor
</%def>
<%def name="row_for_param( param, value, error_dict, prefix, ctx, allow_runtime=True )">
%if error_dict.has_key( param.name ):
<% cls = "form-row form-row-error" %>
%else:
<% cls = "form-row" %>
%endif
<div class="${cls}" id="row-${prefix}${param.name}">
## Data parameters are very special since their value / runtime state
## comes from connectors
%if type( param ) is DataToolParameter:
<label>
${param.get_label()}
</label>
<div>
Data input '${param.name}' (${" or ".join( param.extensions )})
</div>
%elif type( param ) is DataCollectionToolParameter:
<label>
${param.get_label()}
</label>
<div>
Data collection input '${param.name}'
</div>
<%def name="do_inputs( inputs, values, errors, prefix, ctx=None )">
<% ctx = ExpressionContext( values, ctx ) %>
%for input_index, input in enumerate( inputs.itervalues() ):
%if input.type == "repeat":
<div class="repeat-group form-row">
<label>${input.title_plural}:</label>
<% repeat_values = values[input.name] %>
%for i in range( len( repeat_values ) ):
<%
if input.name in errors:
rep_errors = errors[input.name][i]
else:
rep_errors = dict()
index = repeat_values[i]['__index__']
%>
<div class="repeat-group-item">
<div class="form-title-row"><label>${input.title} ${i + 1}</label></div>
${do_inputs( input.inputs, repeat_values[ i ], rep_errors, prefix + input.name + "_" + str(index) + "|", ctx )}
<div class="form-row"><input type="submit" name="${prefix}${input.name}_${index}_remove" value="Remove ${input.title} ${i+1}"></div>
</div>
%endfor
<div class="form-row"><input type="submit" name="${prefix}${input.name}_add" value="Add new ${input.title}"></div>
</div>
%elif input.type == "conditional":
%if input.is_job_resource_conditional:
<% continue %>
%endif
<% group_values = values[input.name] %>
<% current_case = group_values['__current_case__'] %>
<% group_prefix = prefix + input.name + "|" %>
<% group_errors = errors.get( input.name, {} ) %>
${row_for_param( input.test_param, group_values[ input.test_param.name ], group_errors, group_prefix, ctx, allow_runtime=False )}
${do_inputs( input.cases[ current_case ].inputs, group_values, group_errors, group_prefix, ctx )}
%else:
%if isinstance( value, RuntimeValue ):
%if input.name in values:
${row_for_param( input, values[ input.name ], errors, prefix, ctx )}
%else:
<% errors[ input.name ] = 'Value not stored, displaying default' %>
${row_for_param( input, input.get_initial_value( trans, values ), errors, prefix, ctx )}
%endif
%endif
%endfor
</%def>
<%def name="row_for_param( param, value, error_dict, prefix, ctx, allow_runtime=True )">
%if error_dict.has_key( param.name ):
<% cls = "form-row form-row-error" %>
%else:
<% cls = "form-row" %>
%endif
<div class="${cls}" id="row-${prefix}${param.name}">
## Data parameters are very special since their value / runtime state
## comes from connectors
%if type( param ) is DataToolParameter:
<label>
${param.get_label()}:
<span class="popupmenu">
<button type="submit" name="make_buildtime" value="${prefix}${param.name}">Set in advance</button>
</span>
${param.get_label()}
</label>
<div>
<i>To be set at runtime</i>
Data input '${param.name}' (${" or ".join( param.extensions )})
</div>
%elif type( param ) is DataCollectionToolParameter:
<label>
${param.get_label()}
</label>
<div>
Data collection input '${param.name}'
</div>
%else:
<label>
${param.get_label()}:
%if allow_runtime:
%if isinstance( value, RuntimeValue ):
<label>
${param.get_label()}:
<span class="popupmenu">
<button type="submit" name="make_runtime" value="${prefix}${param.name}">Set at runtime</button>
<button type="submit" name="make_buildtime" value="${prefix}${param.name}">Set in advance</button>
</span>
%endif
</label>
<div>
${param.get_html_field( trans, value, ctx ).get_html( prefix )}
</div>
%endif
%if error_dict.has_key( param.name ):
<div style="color: red; font-weight: bold; padding-top: 1px; padding-bottom: 3px;">
<div style="width: 300px;"><img style="vertical-align: middle;" src="${h.url_for('/static/style/error_small.png')}">&nbsp;<span style="vertical-align: middle;">${error_dict[param.name]}</span></div>
</div>
%endif
%endif
<div style="clear: both"></div>
</div>
</%def>
<form method="post" action="${h.url_for(controller='workflow', action='editor_form_post' )}">
<div class="toolForm">
<div class="toolFormTitle">Tool: ${tool.name}</div>
%if tool.version:
<div class="form-row"><div class='titleRow'>Version: ${tool.version}</div></div>
%endif
<div class="toolFormBody">
<input type="hidden" name="tool_id" value="${tool.id}" />
%for i, inputs in enumerate( tool.inputs_by_page ):
%if tool.has_multiple_pages:
<div class='titleRow'>Page ${i+1}</div>
</label>
<div>
<i>To be set at runtime</i>
</div>
%else:
<label>
${param.get_label()}:
%if allow_runtime:
<span class="popupmenu">
<button type="submit" name="make_runtime" value="${prefix}${param.name}">Set at runtime</button>
</span>
%endif
</label>
<div>
${param.get_html_field( trans, value, ctx ).get_html( prefix )}
</div>
%endif
${do_inputs( inputs, values, errors, "" )}
%endfor
%if error_dict.has_key( param.name ):
<div style="color: red; font-weight: bold; padding-top: 1px; padding-bottom: 3px;">
<div style="width: 300px;"><img style="vertical-align: middle;" src="${h.url_for('/static/style/error_small.png')}">&nbsp;<span style="vertical-align: middle;">${error_dict[param.name]}</span></div>
</div>
%endif
%endif
<div style="clear: both"></div>
</div>
</div>
</%def>
</form>
<form method="post" action="${h.url_for(controller='workflow', action='editor_form_post' )}">
<div class="toolForm">
<div class="toolFormTitle">Tool: ${tool.name}</div>
%if tool.version:
<div class="form-row"><div class='titleRow'>Version: ${tool.version}</div></div>
%endif
<div class="toolFormBody">
<input type="hidden" name="tool_id" value="${tool.id}" />
%for i, inputs in enumerate( tool.inputs_by_page ):
%if tool.has_multiple_pages:
<div class='titleRow'>Page ${i+1}</div>
%endif
${do_inputs( inputs, values, errors, "" )}
%endfor
</div>
</div>
</form>
%endif