diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py index 26c4303513d..221d25845d2 100644 --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -22,6 +22,7 @@ class Configuration( object ): def __init__( self, **kwargs ): self.config_dict = kwargs self.root = kwargs.get( 'root_dir', '.' ) + self.enable_beta_features = kwargs.get( "enable_beta_features", False ) self.database = resolve_path( kwargs.get( "database_file", "database/universe.d" ), self.root ) self.database_connection = kwargs.get( "database_connection", False ) self.file_path = resolve_path( kwargs.get( "file_path", "database/files" ), self.root ) diff --git a/lib/galaxy/web/base/controller.py b/lib/galaxy/web/base/controller.py index f066aecfa81..6c004562266 100644 --- a/lib/galaxy/web/base/controller.py +++ b/lib/galaxy/web/base/controller.py @@ -15,6 +15,8 @@ class BaseController( object ): """ Base class for Galaxy web application controllers. """ + + beta = False def __init__( self, app ): """Initialize an interface for application 'app'""" diff --git a/lib/galaxy/web/buildapp.py b/lib/galaxy/web/buildapp.py index 2e58d87c04e..c721fc0c578 100644 --- a/lib/galaxy/web/buildapp.py +++ b/lib/galaxy/web/buildapp.py @@ -41,7 +41,8 @@ def add_controllers( webapp, app ): for key in dir( module ): T = getattr( module, key ) if isclass( T ) and T is not BaseController and issubclass( T, BaseController ): - webapp.add_controller( name, T( app ) ) + if app.config.enable_beta_features or not ( T.beta ): + webapp.add_controller( name, T( app ) ) def app_factory( global_conf, **kwargs ): """ diff --git a/lib/galaxy/web/controllers/workflow_editor.py b/lib/galaxy/web/controllers/workflow_editor.py new file mode 100644 index 00000000000..50dff18bdc0 --- /dev/null +++ b/lib/galaxy/web/controllers/workflow_editor.py @@ -0,0 +1,65 @@ +from galaxy.web.base.controller import * + +from galaxy.tools.parameters import DataToolParameter +from galaxy.datatypes.data import Data + +class WorkflowEditor( BaseController ): + beta = True + + @web.expose + def index( self, trans ): + return trans.fill_template( "workflow_editor/index.mako" ) + + @web.expose + def canvas( self, trans ): + return trans.fill_template( "workflow_editor/canvas.mako" ) + + @web.expose + def tool_form( self, trans, tool_id ): + tool = trans.app.toolbox.tools_by_id[tool_id] + return trans.fill_template( "workflow_editor/tool_form.mako", tool=tool, as_html=as_html ) + + @web.json + def get_tool_info( self, trans, tool_id ): + tool = trans.app.toolbox.tools_by_id[tool_id] + rval = {} + rval['name'] = tool.name + data_inputs = [] + for name, input in tool.inputs.iteritems(): + d = dict( data=False, name=input.name, label=input.label, html=as_html( input, None ) ) + if isinstance( input, DataToolParameter ): + d.update( dict( data=True, extensions=input.extensions ) ) + data_inputs.append( d ) + rval['data_inputs'] = data_inputs + data_outputs = [] + for name, ( format, metadata_source, parent ) in tool.outputs.iteritems(): + data_outputs.append( dict( name=name, extension=format ) ) + rval['data_outputs'] = data_outputs + return rval + + @web.json + def get_datatypes( self, trans ): + ext_to_class_name = dict() + classes = [] + for k, v in trans.app.datatypes_registry.datatypes_by_extension.iteritems(): + c = v.__class__ + ext_to_class_name[k] = c.__module__ + "." + c.__name__ + classes.append( c ) + class_to_classes = dict() + def visit_bases( types, cls ): + for base in cls.__bases__: + if issubclass( base, Data ): + types.add( base.__module__ + "." + base.__name__ ) + visit_bases( types, base ) + for c in classes: + n = c.__module__ + "." + c.__name__ + types = set( [ n ] ) + visit_bases( types, c ) + class_to_classes[ n ] = dict( ( t, True ) for t in types ) + return dict( ext_to_class_name=ext_to_class_name, class_to_classes=class_to_classes ) + +def as_html( param, trans ): + if type( param ) is DataToolParameter: + return "Data input '" + param.name + "' (" + ( " or ".join( param.extensions ) ) + ")" + else: + return param.get_html( trans ) \ No newline at end of file diff --git a/static/scripts/jquery.blockUI.js b/static/scripts/jquery.blockUI.js new file mode 100644 index 00000000000..2b0a3608056 --- /dev/null +++ b/static/scripts/jquery.blockUI.js @@ -0,0 +1,361 @@ +/* + * jQuery blockUI plugin + * Version 1.33 (09/14/2007) + * @requires jQuery v1.1.1 + * + * $Id$ + * + * Examples at: http://malsup.com/jquery/block/ + * Copyright (c) 2007 M. Alsup + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + (function($) { +/** + * blockUI provides a mechanism for blocking user interaction with a page (or parts of a page). + * This can be an effective way to simulate synchronous behavior during ajax operations without + * locking the browser. It will prevent user operations for the current page while it is + * active ane will return the page to normal when it is deactivate. blockUI accepts the following + * two optional arguments: + * + * message (String|Element|jQuery): The message to be displayed while the UI is blocked. The message + * argument can be a plain text string like "Processing...", an HTML string like + * "

Please wait...

", a DOM element, or a jQuery object. + * The default message is "

Please wait...

" + * + * css (Object): Object which contains css property/values to override the default styles of + * the message. Use this argument if you wish to override the default + * styles. The css Object should be in a format suitable for the jQuery.css + * function. For example: + * $.blockUI({ + * backgroundColor: '#ff8', + * border: '5px solid #f00, + * fontWeight: 'bold' + * }); + * + * The default blocking message used when blocking the entire page is "

Please wait...

" + * but this can be overridden by assigning a value to $.blockUI.defaults.pageMessage in your + * own code. For example: + * + * $.blockUI.defaults.pageMessage = "

Bitte Wartezeit

"; + * + * The default message styling can also be overridden. For example: + * + * $.extend($.blockUI.defaults.pageMessageCSS, { color: '#00a', backgroundColor: '#0f0' }); + * + * The default styles work well for simple messages like "Please wait", but for longer messages + * style overrides may be necessary. + * + * @example $.blockUI(); + * @desc prevent user interaction with the page (and show the default message of 'Please wait...') + * + * @example $.blockUI( { backgroundColor: '#f00', color: '#fff'} ); + * @desc prevent user interaction and override the default styles of the message to use a white on red color scheme + * + * @example $.blockUI('Processing...'); + * @desc prevent user interaction and display the message "Processing..." instead of the default message + * + * @name blockUI + * @param String|jQuery|Element message Message to display while the UI is blocked + * @param Object css Style object to control look of the message + * @cat Plugins/blockUI + */ +$.blockUI = function(msg, css, opts) { + $.blockUI.impl.install(window, msg, css, opts); +}; + +// expose version number so other plugins can interogate +$.blockUI.version = 1.33; + +/** + * unblockUI removes the UI block that was put in place by blockUI + * + * @example $.unblockUI(); + * @desc unblocks the page + * + * @name unblockUI + * @cat Plugins/blockUI + */ +$.unblockUI = function(opts) { + $.blockUI.impl.remove(window, opts); +}; + +/** + * Blocks user interaction with the selected elements. (Hat tip: Much of + * this logic comes from Brandon Aaron's bgiframe plugin. Thanks, Brandon!) + * By default, no message is displayed when blocking elements. + * + * @example $('div.special').block(); + * @desc prevent user interaction with all div elements with the 'special' class. + * + * @example $('div.special').block('Please wait'); + * @desc prevent user interaction with all div elements with the 'special' class + * and show a message over the blocked content. + * + * @name block + * @type jQuery + * @param String|jQuery|Element message Message to display while the element is blocked + * @param Object css Style object to control look of the message + * @cat Plugins/blockUI + */ +$.fn.block = function(msg, css, opts) { + return this.each(function() { + if (!this.$pos_checked) { + if ($.css(this,"position") == 'static') + this.style.position = 'relative'; + if ($.browser.msie) this.style.zoom = 1; // force 'hasLayout' in IE + this.$pos_checked = 1; + } + $.blockUI.impl.install(this, msg, css, opts); + }); +}; + +/** + * Unblocks content that was blocked by "block()" + * + * @example $('div.special').unblock(); + * @desc unblocks all div elements with the 'special' class. + * + * @name unblock + * @type jQuery + * @cat Plugins/blockUI + */ +$.fn.unblock = function(opts) { + return this.each(function() { + $.blockUI.impl.remove(this, opts); + }); +}; + +/** + * displays the first matched element in a "display box" above a page overlay. + * + * @example $('#myImage').displayBox(); + * @desc displays "myImage" element in a box + * + * @name displayBox + * @type jQuery + * @cat Plugins/blockUI + */ +$.fn.displayBox = function(css, fn, isFlash) { + var msg = this[0]; + if (!msg) return; + var $msg = $(msg); + css = css || {}; + + var w = $msg.width() || $msg.attr('width') || css.width || $.blockUI.defaults.displayBoxCSS.width; + var h = $msg.height() || $msg.attr('height') || css.height || $.blockUI.defaults.displayBoxCSS.height ; + if (w[w.length-1] == '%') { + var ww = document.documentElement.clientWidth || document.body.clientWidth; + w = parseInt(w) || 100; + w = (w * ww) / 100; + } + if (h[h.length-1] == '%') { + var hh = document.documentElement.clientHeight || document.body.clientHeight; + h = parseInt(h) || 100; + h = (h * hh) / 100; + } + + var ml = '-' + parseInt(w)/2 + 'px'; + var mt = '-' + parseInt(h)/2 + 'px'; + + // supress opacity on overlay if displaying flash content on mac/ff platform + var ua = navigator.userAgent.toLowerCase(); + var opts = { + displayMode: fn || 1, + noalpha: isFlash && /mac/.test(ua) && /firefox/.test(ua) + }; + + $.blockUI.impl.install(window, msg, { width: w, height: h, marginTop: mt, marginLeft: ml }, opts); +}; + + +// override these in your code to change the default messages and styles +$.blockUI.defaults = { + // the message displayed when blocking the entire page + pageMessage: '

Please wait...

', + // the message displayed when blocking an element + elementMessage: '', // none + // styles for the overlay iframe + overlayCSS: { backgroundColor: '#fff', opacity: '0.5' }, + // styles for the message when blocking the entire page + pageMessageCSS: { width:'250px', margin:'-50px 0 0 -125px', top:'50%', left:'50%', textAlign:'center', color:'#000', backgroundColor:'#fff', border:'3px solid #aaa' }, + // styles for the message when blocking an element + elementMessageCSS: { width:'250px', padding:'10px', textAlign:'center', backgroundColor:'#fff'}, + // styles for the displayBox + displayBoxCSS: { width: '400px', height: '400px', top:'50%', left:'50%' }, + // allow body element to be stetched in ie6 + ie6Stretch: 1, + // supress tab nav from leaving blocking content? + allowTabToLeave: 0, + // Title attribute for overlay when using displayBox + closeMessage: 'Click to close', + // use fadeOut effect when unblocking (can be overridden on unblock call) + fadeOut: 1, + // fadeOut transition time in millis + fadeTime: 400 +}; + +// the gory details +$.blockUI.impl = { + box: null, + boxCallback: null, + pageBlock: null, + pageBlockEls: [], + op8: window.opera && window.opera.version() < 9, + ie6: $.browser.msie && /MSIE 6.0/.test(navigator.userAgent), + install: function(el, msg, css, opts) { + opts = opts || {}; + this.boxCallback = typeof opts.displayMode == 'function' ? opts.displayMode : null; + this.box = opts.displayMode ? msg : null; + var full = (el == window); + + // use logical settings for opacity support based on browser but allow overrides via opts arg + var noalpha = this.op8 || $.browser.mozilla && /Linux/.test(navigator.platform); + if (typeof opts.alphaOverride != 'undefined') + noalpha = opts.alphaOverride == 0 ? 1 : 0; + + if (full && this.pageBlock) this.remove(window, {fadeOut:0}); + // check to see if we were only passed the css object (a literal) + if (msg && typeof msg == 'object' && !msg.jquery && !msg.nodeType) { + css = msg; + msg = null; + } + msg = msg ? (msg.nodeType ? $(msg) : msg) : full ? $.blockUI.defaults.pageMessage : $.blockUI.defaults.elementMessage; + if (opts.displayMode) + var basecss = jQuery.extend({}, $.blockUI.defaults.displayBoxCSS); + else + var basecss = jQuery.extend({}, full ? $.blockUI.defaults.pageMessageCSS : $.blockUI.defaults.elementMessageCSS); + css = jQuery.extend(basecss, css || {}); + var f = ($.browser.msie) ? $('') + : $(''); + var w = $('
'); + var m = full ? $('
') + : $(''); + w.css('position', full ? 'fixed' : 'absolute'); + if (msg) m.css(css); + if (!noalpha) w.css($.blockUI.defaults.overlayCSS); + if (this.op8) w.css({ width:''+el.clientWidth,height:''+el.clientHeight }); // lame + if ($.browser.msie) f.css('opacity','0.0'); + + $([f[0],w[0],m[0]]).appendTo(full ? 'body' : el); + + // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling) + var expr = $.browser.msie && (!$.boxModel || $('object,embed', full ? null : el).length > 0); + if (this.ie6 || expr) { + // stretch content area if it's short + if (full && $.blockUI.defaults.ie6Stretch && $.boxModel) + $('html,body').css('height','100%'); + + // fix ie6 problem when blocked element has a border width + if ((this.ie6 || !$.boxModel) && !full) { + var t = this.sz(el,'borderTopWidth'), l = this.sz(el,'borderLeftWidth'); + var fixT = t ? '(0 - '+t+')' : 0; + var fixL = l ? '(0 - '+l+')' : 0; + } + + // simulate fixed position + $.each([f,w,m], function(i,o) { + var s = o[0].style; + s.position = 'absolute'; + if (i < 2) { + full ? s.setExpression('height','document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + "px"') + : s.setExpression('height','this.parentNode.offsetHeight + "px"'); + full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"') + : s.setExpression('width','this.parentNode.offsetWidth + "px"'); + if (fixL) s.setExpression('left', fixL); + if (fixT) s.setExpression('top', fixT); + } + else { + if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'); + s.marginTop = 0; + } + }); + } + if (opts.displayMode) { + w.css('cursor','default').attr('title', $.blockUI.defaults.closeMessage); + m.css('cursor','default'); + $([f[0],w[0],m[0]]).removeClass('blockUI').addClass('displayBox'); + $().click($.blockUI.impl.boxHandler).bind('keypress', $.blockUI.impl.boxHandler); + } + else + this.bind(1, el); + m.append(msg).show(); + if (msg.jquery) msg.show(); + if (opts.displayMode) return; + if (full) { + this.pageBlock = m[0]; + this.pageBlockEls = $(':input:enabled:visible',this.pageBlock); + setTimeout(this.focus, 20); + } + else this.center(m[0]); + }, + remove: function(el, opts) { + var o = $.extend({}, $.blockUI.defaults, opts); + this.bind(0, el); + var full = el == window; + var els = full ? $('body').children().filter('.blockUI') : $('.blockUI', el); + if (full) this.pageBlock = this.pageBlockEls = null; + + if (o.fadeOut) { + els.fadeOut(o.fadeTime, function() { + if (this.parentNode) this.parentNode.removeChild(this); + }); + } + else els.remove(); + }, + boxRemove: function(el) { + $().unbind('click',$.blockUI.impl.boxHandler).unbind('keypress', $.blockUI.impl.boxHandler); + if (this.boxCallback) + this.boxCallback(this.box); + $('body .displayBox').hide().remove(); + }, + // event handler to suppress keyboard/mouse events when blocking + handler: function(e) { + if (e.keyCode && e.keyCode == 9) { + if ($.blockUI.impl.pageBlock && !$.blockUI.defaults.allowTabToLeave) { + var els = $.blockUI.impl.pageBlockEls; + var fwd = !e.shiftKey && e.target == els[els.length-1]; + var back = e.shiftKey && e.target == els[0]; + if (fwd || back) { + setTimeout(function(){$.blockUI.impl.focus(back)},10); + return false; + } + } + } + if ($(e.target).parents('div.blockMsg').length > 0) + return true; + return $(e.target).parents().children().filter('div.blockUI').length == 0; + }, + boxHandler: function(e) { + if ((e.keyCode && e.keyCode == 27) || (e.type == 'click' && $(e.target).parents('div.blockMsg').length == 0)) + $.blockUI.impl.boxRemove(); + return true; + }, + // bind/unbind the handler + bind: function(b, el) { + var full = el == window; + // don't bother unbinding if there is nothing to unbind + if (!b && (full && !this.pageBlock || !full && !el.$blocked)) return; + if (!full) el.$blocked = b; + var $e = $(el).find('a,:input'); + $.each(['mousedown','mouseup','keydown','keypress','click'], function(i,o) { + $e[b?'bind':'unbind'](o, $.blockUI.impl.handler); + }); + }, + focus: function(back) { + if (!$.blockUI.impl.pageBlockEls) return; + var e = $.blockUI.impl.pageBlockEls[back===true ? $.blockUI.impl.pageBlockEls.length-1 : 0]; + if (e) e.focus(); + }, + center: function(el) { + var p = el.parentNode, s = el.style; + var l = ((p.offsetWidth - el.offsetWidth)/2) - this.sz(p,'borderLeftWidth'); + var t = ((p.offsetHeight - el.offsetHeight)/2) - this.sz(p,'borderTopWidth'); + s.left = l > 0 ? (l+'px') : '0'; + s.top = t > 0 ? (t+'px') : '0'; + }, + sz: function(el, p) { return parseInt($.css(el,p))||0; } +}; + +})(jQuery); diff --git a/templates/workflow_editor/canvas.mako b/templates/workflow_editor/canvas.mako new file mode 100644 index 00000000000..884fd34443f --- /dev/null +++ b/templates/workflow_editor/canvas.mako @@ -0,0 +1,396 @@ + + + + + + + + + + + + + + + + + + + + + + + + ##
+ + + \ No newline at end of file diff --git a/templates/workflow_editor/index.mako b/templates/workflow_editor/index.mako new file mode 100644 index 00000000000..673125ade43 --- /dev/null +++ b/templates/workflow_editor/index.mako @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + ##
+ ## + ##
+
+
+
+ %for i, section in enumerate( app.toolbox.sections ): + %if i > 0: +
+ %endif +
+ ${section.name} +
+
+
+ %for tool in section.tools: + %if not tool.hidden: +
+ ## #if $tool.input_required + ## #set $link = $h.url_for( 'tool_runner', tool_id=$tool.id ) + ## #else + ## #set $link = $h.url_for( $tool.action, ** $tool.get_static_param_values( $t ) ) + ## #end if + %if "[[" in tool.description and "]]" in tool.description: + ${tool.description.replace( '[[', '' % tool.id ).replace( "]]", "" )} + %elif tool.name: + ${tool.name} ${tool.description} + %else: + ${tool.description} + %endif +
+ %endif + %endfor +
+
+ %endfor +
+
+
+
+
+