Howdy devs, I've implemented some rather basic tool access control and am looking for feedback on my implementation.
# Why Our organisation wanted the ability to restrict tools to different users/roles. As such I've implemented as an "execute" tag which can be applied to either <section> or <tools> in the tool configuration file. # Example galaxy-admin changes For example: <section execute="[email protected],[email protected]" id="EncodeTools" name="ENCODE Tools"> <tool file="encode/gencode_partition.xml" /> <tool execute="[email protected]" file="encode/random_intervals.xml" /> </section> which would allow A and B to access gencode_parition, but only B would be able to access random_intervals. To put it explicity - by default, everyone can access all tools - if section level permissions are set, then those are set as defaults for all tools in that section - if tool permissions are set, they will override the defaults. # Pros and Cons There are some good features - non-accessible tools won't show up in the left hand panel, based on user - non-accessible tools cannot be run or accessed. There are some caveats however. - existence of tools is not completely hidden. - Labels are not hidden at all. - workflows break completely if a tool is unavailable to a shared user and the user copies+edits. They can be copied, and viewed (says tool not found), but cannot be edited. Tool names/id/version info can be found in the javascript object due to the call to app.toolbox.tool_panel.items() in templates/webapps/galaxy/workflow/editor.mako, as that returns the raw tool list, rather than one that's filtered on whether or not the user has access. I'm yet to figure out a clean fix for this. Additionally, empty sections are still shown even if there aren't tools listed in them. For a brief overview of my changes, please see the attached diff. (It's missing one change because I wasn't being careful and started work on multiple different features) # Changeset overview In brief, most of the changes consist of - new method in model.User to check if an array of roles overlaps at all with a user's roles - modifications to appropriate files for reading in the new tool_config.xml's options - modification to get_tool to pass user information, as whether or not a tool exists is now dependent on who is asking. Please let me know if you have input on this before I create a pull request on this feature. # Fixes I believe this will fix a number of previously brought up issues (at least to my understanding of the issues listed) + https://trello.com/c/Zo7FAXlM/286-24-add-ability-to-password-secure-tools + (I saw some solution where they were adding "_beta" to tool names which gave permissions to developers somewhere, but cannot find that now) Cheers, Eric Rasche -- Eric Rasche Programmer II Center for Phage Technology Texas A&M University College Station, TX 77843 404-692-2048 [email protected] [email protected]
diff -r c458a0fe1ba8 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/model/__init__.py Wed Nov 06 11:18:10 2013 -0600
@@ -114,6 +114,19 @@
roles.append( role )
return roles
+ def can_execute( self, permissions=None ):
+ """
+ Check if any of a user's roles overlap with set permissions
+ """
+ # If permissions variable is NOT set, then allow access (be friendly mode)
+ if permissions is None:
+ return True
+ # Otherwise, we want to check and deny if they're not in the set
+ for role in self.all_roles():
+ if role.name in permissions:
+ return True
+ return False
+
def get_disk_usage( self, nice_size=False ):
"""
Return byte count of disk space used by user or a human-readable
diff -r c458a0fe1ba8 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/tools/__init__.py Wed Nov 06 11:18:10 2013 -0600
@@ -195,12 +195,19 @@
self.index += 1
if parsing_shed_tool_conf:
config_elems.append( elem )
+ permissions = None
+ try:
+ permissions = elem.get('execute').split(',')
+ log.debug('Execute section found: %s' % (':'.join(permissions)))
+ except:
+ log.debug("No execute section found")
+ pass
if elem.tag == 'tool':
- self.load_tool_tag_set( elem, self.tool_panel, self.integrated_tool_panel, tool_path, load_panel_dict, guid=elem.get( 'guid' ), index=index )
+ self.load_tool_tag_set( elem, self.tool_panel, self.integrated_tool_panel, tool_path, load_panel_dict, guid=elem.get( 'guid' ), index=index, permissions=permissions )
elif elem.tag == 'workflow':
self.load_workflow_tag_set( elem, self.tool_panel, self.integrated_tool_panel, load_panel_dict, index=index )
elif elem.tag == 'section':
- self.load_section_tag_set( elem, tool_path, load_panel_dict, index=index )
+ self.load_section_tag_set( elem, tool_path, load_panel_dict, index=index, permissions=permissions )
elif elem.tag == 'label':
self.load_label_tag_set( elem, self.tool_panel, self.integrated_tool_panel, load_panel_dict, index=index )
if parsing_shed_tool_conf:
@@ -365,11 +372,16 @@
shutil.move( filename, os.path.abspath( self.integrated_tool_panel_config ) )
os.chmod( self.integrated_tool_panel_config, 0644 )
- def get_tool( self, tool_id, tool_version=None, get_all_versions=False ):
+ def get_tool( self, tool_id, tool_version=None, get_all_versions=False, user=None):
"""Attempt to locate a tool in the tool box."""
+ log.debug("Getting tool %s" % (tool_id))
if tool_id in self.tools_by_id and not get_all_versions:
#tool_id exactly matches an available tool by id (which is 'old' tool_id or guid)
- return self.tools_by_id[ tool_id ]
+ tool = self.tools_by_id[ tool_id ]
+ if user is not None and user.can_execute(tool.permissions):
+ return self.tools_by_id[ tool_id ]
+ else:
+ return None
#exact tool id match not found, or all versions requested, search for other options, e.g. migrated tools or different versions
rval = []
tv = self.__get_tool_version( tool_id )
@@ -377,12 +389,15 @@
tool_version_ids = tv.get_version_ids( self.app )
for tool_version_id in tool_version_ids:
if tool_version_id in self.tools_by_id:
- rval.append( self.tools_by_id[ tool_version_id ] )
+ tool = self.tools_by_id[ tool_version_id ]
+ if user is not None and user.can_execute(tool.permissions):
+ rval.append( tool )
if not rval:
#still no tool, do a deeper search and try to match by old ids
for tool in self.tools_by_id.itervalues():
if tool.old_id == tool_id:
- rval.append( tool )
+ if user is not None and user.can_execute(tool.permissions):
+ rval.append( tool )
if rval:
if get_all_versions:
return rval
@@ -390,7 +405,7 @@
if tool_version:
#return first tool with matching version
for tool in rval:
- if tool.version == tool_version:
+ if tool.version == tool_version and user is not None and user.can_execute(tool.permissions):
return tool
#No tool matches by version, simply return the first available tool found
return rval[0]
@@ -464,7 +479,7 @@
select_field.add_option( 'version %s' % option_tup[0], option_tup[1] )
return select_field
- def load_tool_tag_set( self, elem, panel_dict, integrated_panel_dict, tool_path, load_panel_dict, guid=None, index=None ):
+ def load_tool_tag_set( self, elem, panel_dict, integrated_panel_dict, tool_path, load_panel_dict, guid=None, index=None, permissions=None ):
try:
path = elem.get( "file" )
repository_id = None
@@ -492,6 +507,16 @@
# a new repository, so any included tools can be loaded into the tool panel.
can_load_into_panel_dict = True
tool = self.load_tool( os.path.join( tool_path, path ), guid=guid, repository_id=repository_id )
+ try:
+ # If we have special permissions on this element, use those
+ tool.permissions = elem.get("execute").split(',')
+ except:
+ # Otherwise, we'll use the parent's permissions
+ tool.permissions = permissions
+ if tool.permissions is not None:
+ log.debug("Loaded tool %s with permissions %s" % ( tool.id, ','.join(tool.permissions)))
+ else:
+ log.debug("Loaded tool %s" % (tool.id))
key = 'tool_%s' % str( tool.id )
if can_load_into_panel_dict:
if guid is not None:
@@ -572,7 +597,7 @@
else:
integrated_panel_dict.insert( index, key, label )
- def load_section_tag_set( self, elem, tool_path, load_panel_dict, index=None ):
+ def load_section_tag_set( self, elem, tool_path, load_panel_dict, index=None, permissions=None ):
key = 'section_' + elem.get( "id" )
if key in self.tool_panel:
section = self.tool_panel[ key ]
@@ -588,11 +613,13 @@
integrated_elems = integrated_section.elems
for sub_index, sub_elem in enumerate( elem ):
if sub_elem.tag == 'tool':
- self.load_tool_tag_set( sub_elem, elems, integrated_elems, tool_path, load_panel_dict, guid=sub_elem.get( 'guid' ), index=sub_index )
+ self.load_tool_tag_set( sub_elem, elems, integrated_elems, tool_path, load_panel_dict, guid=sub_elem.get( 'guid' ), index=sub_index, permissions=permissions )
elif sub_elem.tag == 'workflow':
self.load_workflow_tag_set( sub_elem, elems, integrated_elems, load_panel_dict, index=sub_index )
elif sub_elem.tag == 'label':
self.load_label_tag_set( sub_elem, elems, integrated_elems, load_panel_dict, index=sub_index )
+ # If we have permissions, store those so we don't display the section as well as hiding everything below
+ section.permissions = permissions
if load_panel_dict:
self.tool_panel[ key ] = section
# Always load sections into the integrated_tool_panel.
@@ -716,7 +743,6 @@
"""
to_dict toolbox.
"""
-
context = Bunch( toolbox=self, trans=trans, **kwds )
if in_panel:
panel_elts = [ val for val in self.tool_panel.itervalues() ]
@@ -737,11 +763,17 @@
link_details = True
)
for elt in panel_elts:
- rval.append( to_dict_helper( elt, kwargs ) )
+ # If the user is allowed to see it, let them
+ #if elt.can_user_execute( trans ):
+ if trans.user.can_execute( elt.permissions ):
+ rval.append( to_dict_helper( elt, kwargs ) )
else:
tools = []
for id, tool in self.tools_by_id.items():
- tools.append( tool.to_dict( trans, link_details=True ) )
+ # If the user is allowed to see it, let them
+ #if tool.can_user_execute( trans ):
+ if trans.user.can_execute( tool.permissions ):
+ tools.append( tool.to_dict( trans, link_details=True ) )
rval = tools
return rval
@@ -816,6 +848,7 @@
self.id = f( elem, 'id' )
self.version = f( elem, 'version' )
self.elems = odict()
+ self.permissions = None
def copy( self ):
copy = ToolSection()
@@ -869,6 +902,8 @@
"""
# Convert parameters to a dictionary of strings, and save curent
# page in that dict
+ if not tool:
+ return None
value = params_to_strings( tool.inputs, self.inputs, app )
value["__page__"] = self.page
value["__rerun_remap_job_id__"] = self.rerun_remap_job_id
@@ -996,6 +1031,8 @@
# Parse XML element containing configuration
self.parse( root, guid=guid )
self.external_runJob_script = app.config.drmaa_external_runjob_script
+ self.permissions = None
+
@property
def sa_session( self ):
"""Returns a SQLAlchemy session"""
@@ -2954,6 +2991,7 @@
return trans.get_history( create=create )
+
class OutputParameterJSONTool( Tool ):
"""
Alternate implementation of Tool that provides parameters and other values
diff -r c458a0fe1ba8 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/web/base/controller.py Wed Nov 06 11:18:10 2013 -0600
@@ -1140,7 +1140,7 @@
job = self.get_hda_job( hda )
if not job:
return None
- tool = trans.app.toolbox.get_tool( job.tool_id )
+ tool = trans.app.toolbox.get_tool( job.tool_id, user=trans.user )
if not tool:
return None
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/api/tools.py
--- a/lib/galaxy/webapps/galaxy/api/tools.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/api/tools.py Wed Nov 06 11:18:10 2013 -0600
@@ -75,7 +75,7 @@
# -- Execute tool. --
# Get tool.
- tool = trans.app.toolbox.get_tool( payload[ 'tool_id' ] ) if 'tool_id' in payload else None
+ tool = trans.app.toolbox.get_tool( payload[ 'tool_id' ], user=trans.user ) if 'tool_id' in payload else None
if not tool:
trans.response.status = 404
return { "message": { "type": "error", "text" : trans.app.model.Dataset.conversion_messages.NO_TOOL } }
@@ -167,7 +167,7 @@
#
# Execute tool.
#
- tool = trans.app.toolbox.get_tool( tool_id )
+ tool = trans.app.toolbox.get_tool( tool_id, trans=trans.user )
if not tool:
return trans.app.model.Dataset.conversion_messages.NO_TOOL
@@ -242,7 +242,7 @@
# have priority.
#
original_job = self.get_hda_job( original_dataset )
- tool = trans.app.toolbox.get_tool( original_job.tool_id )
+ tool = trans.app.toolbox.get_tool( original_job.tool_id, user=trans.user )
if not tool:
return trans.app.model.Dataset.conversion_messages.NO_TOOL
tool_params = dict( [ ( p.name, p.value ) for p in original_job.parameters ] )
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/api/workflows.py
--- a/lib/galaxy/webapps/galaxy/api/workflows.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/api/workflows.py Wed Nov 06 11:18:10 2013 -0600
@@ -229,7 +229,7 @@
for i, step in enumerate( workflow.steps ):
job = None
if step.type == 'tool' or step.type is None:
- tool = self.app.toolbox.get_tool( step.tool_id )
+ tool = self.app.toolbox.get_tool( step.tool_id, user=self.user )
def callback( input, value, prefixed_name, prefixed_label ):
if isinstance( input, DataToolParameter ):
if prefixed_name in step.input_connections_by_name:
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/controllers/async.py
--- a/lib/galaxy/webapps/galaxy/controllers/async.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/controllers/async.py Wed Nov 06 11:18:10 2013 -0600
@@ -42,7 +42,7 @@
# initialize the tool
toolbox = self.get_toolbox()
- tool = toolbox.get_tool( tool_id )
+ tool = toolbox.get_tool( tool_id, user=trans.user )
if not tool:
return "Tool with id %s not found" % tool_id
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py Wed Nov 06 11:18:10 2013 -0600
@@ -1001,7 +1001,7 @@
try:
# Load the tool
toolbox = self.get_toolbox()
- tool = toolbox.get_tool( job.tool_id )
+ tool = toolbox.get_tool( job.tool_id, user=trans.user )
assert tool is not None, 'Requested tool has not been loaded.'
#Load parameter objects, if a parameter type has changed, its possible for the value to no longer be valid
try:
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py Wed Nov 06 11:18:10 2013 -0600
@@ -669,7 +669,7 @@
#.add_input( "file", "Archived History File", "archive_file", value=None, error=None )
)
# Run job to do import.
- history_imp_tool = trans.app.toolbox.get_tool( '__IMPORT_HISTORY__' )
+ history_imp_tool = trans.app.toolbox.get_tool( '__IMPORT_HISTORY__', user=trans.user )
incoming = { '__ARCHIVE_SOURCE__' : archive_source, '__ARCHIVE_TYPE__' : archive_type }
history_imp_tool.execute( trans, incoming=incoming )
return trans.show_message( "Importing history from '%s'. \
@@ -726,7 +726,7 @@
% ( { 'n' : history.name, 's' : url_for( controller='history', action="export_archive", id=id, qualified=True ) } ) )
# Run job to do export.
- history_exp_tool = trans.app.toolbox.get_tool( '__EXPORT_HISTORY__' )
+ history_exp_tool = trans.app.toolbox.get_tool( '__EXPORT_HISTORY__',user=trans.user )
params = {
'history_to_export' : history,
'compress' : gzip,
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/controllers/library_common.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_common.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/controllers/library_common.py Wed Nov 06 11:18:10 2013 -0600
@@ -1020,7 +1020,7 @@
def upload_dataset( self, trans, cntrller, library_id, folder_id, replace_dataset=None, **kwd ):
# Set up the traditional tool state/params
tool_id = 'upload1'
- tool = trans.app.toolbox.get_tool( tool_id )
+ tool = trans.app.toolbox.get_tool( tool_id, user=trans.user )
state = tool.new_state( trans )
errors = tool.update_state( trans, tool.inputs_by_page[0], state.inputs, kwd )
tool_params = state.inputs
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/controllers/root.py
--- a/lib/galaxy/webapps/galaxy/controllers/root.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/controllers/root.py Wed Nov 06 11:18:10 2013 -0600
@@ -77,7 +77,7 @@
"""Return help page for tool identified by 'id' if available
"""
toolbox = self.get_toolbox()
- tool = toolbox.get_tool( id )
+ tool = toolbox.get_tool( id, user=trans.user )
yield "<html><body>"
if not tool:
#TODO: arent tool ids strings now?
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py Wed Nov 06 11:18:10 2013 -0600
@@ -270,7 +270,7 @@
else:
library_bunch = None
return upload_common.new_upload( trans, cntrller, ud, library_bunch=library_bunch, state=trans.app.model.HistoryDatasetAssociation.states.UPLOAD )
- tool = self.get_toolbox().get_tool( tool_id )
+ tool = self.get_toolbox().get_tool( tool_id, user=trans.user )
if not tool:
return False # bad tool_id
nonfile_params = galaxy.util.Params( kwd, sanitize=False )
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py Wed Nov 06 11:18:10 2013 -0600
@@ -1301,7 +1301,7 @@
filter( self.app.model.History.user==trans.user ). \
order_by( self.app.model.Job.create_time.desc() ).limit(1)
tool_id = query[0][0] # Get first element in first row of query.
- tool = self.get_toolbox().get_tool( tool_id )
+ tool = self.get_toolbox().get_tool( tool_id, user=trans.user )
# Return tool info.
tool_info = {
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/controllers/visualization.py
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py Wed Nov 06 11:18:10 2013 -0600
@@ -872,7 +872,7 @@
}
# Add tool, dataset attributes to config based on id.
- tool = trans.app.toolbox.get_tool( viz_config[ 'tool_id' ] )
+ tool = trans.app.toolbox.get_tool( viz_config[ 'tool_id' ], user=trans.user )
viz_config[ 'tool' ] = tool.to_dict( trans, io_details=True )
viz_config[ 'dataset' ] = trans.security.encode_dict_ids( dataset.to_dict() )
diff -r c458a0fe1ba8 lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py Wed Nov 06 11:18:10 2013 -0600
@@ -1229,7 +1229,7 @@
for job_id in job_ids:
assert job_id in jobs_by_id, "Attempt to create workflow with job not connected to current history"
job = jobs_by_id[ job_id ]
- tool = trans.app.toolbox.get_tool( job.tool_id )
+ tool = trans.app.toolbox.get_tool( job.tool_id, user=trans.user )
param_values = job.get_param_values( trans.app, ignore_errors=True ) #If a tool was updated and e.g. had a text value changed to an integer, we don't want a traceback here
associations = cleanup_param_values( tool.inputs, param_values )
step = model.WorkflowStep()
@@ -1389,7 +1389,7 @@
# Execute module
job = None
if step.type == 'tool' or step.type is None:
- tool = trans.app.toolbox.get_tool( step.tool_id )
+ tool = trans.app.toolbox.get_tool( step.tool_id, user=trans.user )
# Connect up
def callback( input, value, prefixed_name, prefixed_label ):
replacement = None
diff -r c458a0fe1ba8 lib/galaxy/workflow/modules.py
--- a/lib/galaxy/workflow/modules.py Mon Nov 04 14:56:57 2013 -0500
+++ b/lib/galaxy/workflow/modules.py Wed Nov 06 11:18:10 2013 -0600
@@ -197,7 +197,7 @@
def __init__( self, trans, tool_id ):
self.trans = trans
self.tool_id = tool_id
- self.tool = trans.app.toolbox.get_tool( tool_id )
+ self.tool = trans.app.toolbox.get_tool( tool_id, user=trans.user )
self.post_job_actions = {}
self.workflow_outputs = []
self.state = None
@@ -236,7 +236,7 @@
# TODO: If workflows are ever enhanced to use tool version
# in addition to tool id, enhance the selection process here
# to retrieve the correct version of the tool.
- tool = trans.app.toolbox.get_tool( tool_id )
+ tool = trans.app.toolbox.get_tool( tool_id, user=trans.user )
if tool:
tool_id = tool.id
if ( trans.app.toolbox and tool_id in trans.app.toolbox.tools_by_id ):
@@ -246,9 +246,10 @@
return module_factory.from_dict(trans, from_json_string(step.config), secure=False)
module = Class( trans, tool_id )
module.state = galaxy.tools.DefaultToolState()
- if step.tool_version and (step.tool_version != module.tool.version):
- module.version_changes.append("%s: using version '%s' instead of version '%s' indicated in this workflow." % (tool_id, module.tool.version, step.tool_version))
- module.state.inputs = module.tool.params_from_strings( step.tool_inputs, trans.app, ignore_errors=True )
+ if module.tool is not None:
+ if step.tool_version and (step.tool_version != module.tool.version):
+ module.version_changes.append("%s: using version '%s' instead of version '%s' indicated in this workflow." % (tool_id, module.tool.version, step.tool_version))
+ module.state.inputs = module.tool.params_from_strings( step.tool_inputs, trans.app, ignore_errors=True )
module.errors = step.tool_errors
module.workflow_outputs = step.workflow_outputs
pjadict = {}
@@ -305,7 +306,7 @@
return self.errors
def get_tooltip( self, static_path='' ):
- if self.tool.help:
+ if self.tool and self.tool.help:
return self.tool.help.render( static_path=static_path )
else:
return None
@@ -320,13 +321,17 @@
label=prefixed_label,
multiple=input.multiple,
extensions=input.extensions ) )
-
- visit_input_values( self.tool.inputs, self.state.inputs, callback )
- return data_inputs
+ if self.tool:
+ visit_input_values( self.tool.inputs, self.state.inputs, callback )
+ return data_inputs
+ else:
+ return None
def get_data_outputs( self ):
data_outputs = []
data_inputs = None
+ if self.tool is None:
+ return None
for name, tool_output in self.tool.outputs.iteritems():
if tool_output.format_source != None:
formats = [ 'input' ] # default to special name "input" which remove restrictions on connections
@@ -353,8 +358,11 @@
def get_config_form( self ):
self.add_dummy_datasets()
- return self.trans.fill_template( "workflow/editor_tool_form.mako",
- tool=self.tool, values=self.state.inputs, errors=( self.errors or {} ) )
+ if self.tool:
+ return self.trans.fill_template( "workflow/editor_tool_form.mako",
+ tool=self.tool, values=self.state.inputs, errors=( self.errors or {} ) )
+ else:
+ return self.trans.fill_template( "workflow/editor_tool_form.mako" )
def update_state( self, incoming ):
# Build a callback that handles setting an input to be required at
@@ -389,7 +397,9 @@
self.errors = errors or None
def check_and_update_state( self ):
- return self.tool.check_and_update_param_values( self.state.inputs, self.trans, allow_workflow_parameters=True )
+ if self.tool:
+ return self.tool.check_and_update_param_values( self.state.inputs, self.trans, allow_workflow_parameters=True )
+ return None
def add_dummy_datasets( self, connections=None):
if connections:
@@ -411,7 +421,8 @@
replacement = DummyDataset()
return replacement
- visit_input_values( self.tool.inputs, self.state.inputs, callback )
+ if self.tool is not None:
+ visit_input_values( self.tool.inputs, self.state.inputs, callback )
class WorkflowModuleFactory( object ):
diff -r c458a0fe1ba8 templates/webapps/galaxy/workflow/editor_tool_form.mako
--- a/templates/webapps/galaxy/workflow/editor_tool_form.mako Mon Nov 04 14:56:57 2013 -0500
+++ b/templates/webapps/galaxy/workflow/editor_tool_form.mako Wed Nov 06 11:18:10 2013 -0600
@@ -95,7 +95,7 @@
</%def>
<form method="post" action="${h.url_for(controller='workflow', action='editor_form_post' )}">
-
+ %if tool:
<div class="toolForm">
<div class="toolFormTitle">Tool: ${tool.name}</div>
%if tool.version:
@@ -111,6 +111,6 @@
%endfor
</div>
</div>
-
+ %endif
</form>
signature.asc
Description: OpenPGP digital signature
___________________________________________________________ Please keep all replies on the list by using "reply all" in your mail client. To manage your subscriptions to this and other Galaxy lists, please use the interface at: http://lists.bx.psu.edu/ To search Galaxy mailing lists use the unified search at: http://galaxyproject.org/search/mailinglists/
