diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py
index 8d71f18a678..15737ce77b4 100644
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -538,6 +538,11 @@ class WorkflowStepConnection( object ):
self.input_step_id = None
self.input_name = None
+class StoredWorkflowUserShareAssociation( object ):
+ def __init__( self ):
+ self.stored_workflow = None
+ self.user = None
+
## ---- Utility methods -------------------------------------------------------
def directory_hash_id( id ):
diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py
index 27d8d4342f6..dd15ec11338 100644
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -223,6 +223,12 @@ WorkflowStepConnection.table = Table( "workflow_step_connection", metadata,
Column( "input_name", String)
)
+StoredWorkflowUserShareAssociation.table = Table( "stored_workflow_user_share_connection", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True ),
+ Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True )
+ )
+
# With the tables defined we can define the mappers and setup the
# relationships between the model objects.
@@ -327,6 +333,12 @@ assign_mapper( context, StoredWorkflow, StoredWorkflow.table,
primaryjoin=( StoredWorkflow.table.c.latest_workflow_id == Workflow.table.c.id ) )
) )
+assign_mapper( context, StoredWorkflowUserShareAssociation, StoredWorkflowUserShareAssociation.table,
+ properties=dict( user=relation( User, backref='workflows_shared_by_others' ),
+ stored_workflow=relation( StoredWorkflow )
+ ) )
+
+
def db_next_hid( self ):
"""
Override __next_hid to generate from the database in a concurrency
diff --git a/lib/galaxy/web/base/controller.py b/lib/galaxy/web/base/controller.py
index fac5e8e8d44..30b77890310 100644
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -6,7 +6,7 @@ import os, time, logging
# Pieces of Galaxy to make global in every controller
from galaxy import config, tools, web, model, util
-from galaxy.web import error
+from galaxy.web import error, url_for
from Cheetah.Template import Template
diff --git a/lib/galaxy/web/controllers/workflow.py b/lib/galaxy/web/controllers/workflow.py
index b885285ce71..8713bd37dbd 100644
--- a/lib/galaxy/web/controllers/workflow.py
+++ b/lib/galaxy/web/controllers/workflow.py
@@ -22,7 +22,82 @@ class WorkflowController( BaseController ):
"""
user = trans.get_user()
workflows = trans.sa_session.query( model.StoredWorkflow ).filter_by( user=user, deleted=False ).all()
- return trans.fill_template( "workflow/index.mako", workflows = workflows )
+ shared_by_others = trans.sa_session \
+ .query( model.StoredWorkflowUserShareAssociation ) \
+ .filter_by( user=user ) \
+ .filter( model.StoredWorkflow.c.deleted == False ) \
+ .all()
+ return trans.fill_template( "workflow/index.mako",
+ workflows = workflows,
+ shared_by_others = shared_by_others )
+
+ @web.expose
+ @web.require_login( "use Galaxy workflows" )
+ def share( self, trans, id, email="" ):
+ msg = mtype = None
+ # Load workflow from database
+ stored = get_stored_workflow( trans, id )
+ if email:
+ other = model.User.get_by( email=email )
+ if not other:
+ mtype = "error"
+ msg = ( "User '%s' does not exist" % email )
+ elif trans.sa_session.query( model.StoredWorkflowUserShareAssociation ) \
+ .filter_by( user=other, stored_workflow=stored ).count() > 0:
+ mtype = "error"
+ msg = ( "Workflow already shared with '%s'" % email )
+ else:
+
+ share = model.StoredWorkflowUserShareAssociation()
+ share.stored_workflow = stored
+ share.user = other
+ session = trans.sa_session
+ session.save( share )
+ session.flush()
+ ## trans.template_context['message'] = "Workflow '%s' shared with user '%s'" % ( stored.name, other.email )
+ ## return trans.send_redirect( url_for( controller='workflow', action='index' ) )
+ return trans.show_message( "Workflow '%s' shared with user '%s'" % ( stored.name, other.email ) )
+ return trans.fill_template( "workflow/share.mako",
+ message = msg,
+ messagetype = mtype,
+ stored=stored,
+ email=email )
+
+ @web.expose
+ @web.require_login( "use Galaxy workflows" )
+ def rename( self, trans, id, new_name=None ):
+ stored = get_stored_workflow( trans, id )
+ if new_name is not None:
+ stored.name = new_name
+ trans.sa_session.flush()
+ return trans.response.send_redirect( url_for( controller='workflow', action='index' ) )
+ else:
+ return trans.fill_template( "workflow/rename.mako", stored=stored )
+
+ @web.expose
+ @web.require_login( "use Galaxy workflows" )
+ def clone( self, trans, id ):
+ stored = get_stored_workflow( trans, id, check_ownership=False )
+ user = trans.get_user()
+ if stored.user == user:
+ owner = True
+ else:
+ if trans.sa_session.query( model.StoredWorkflowUserShareAssociation ) \
+ .filter_by( user=user, stored_workflow=stored ).count() == 0:
+ error( "Workflow is not owned by or shared with current user" )
+ owner = False
+ new_stored = model.StoredWorkflow()
+ new_stored.name = "Clone of '%s'" % stored.name
+ new_stored.latest_workflow = stored.latest_workflow
+ if not owner:
+ new_stored.name += "shared by '%s'" % stored.user.email
+ new_stored.user = user
+ # Persist
+ session = trans.sa_session
+ session.save( new_stored )
+ session.flush()
+ # Display the management page
+ return trans.response.send_redirect( url_for( controller='workflow', action='index' ) )
@web.expose
@web.require_login( "create workflows" )
@@ -47,7 +122,7 @@ class WorkflowController( BaseController ):
session.save( stored_workflow )
session.flush()
# Display the management page
- return self.index( trans )
+ return trans.response.send_redirect( url_for( controller='workflow', action='index' ) )
@web.expose
def delete( self, trans, id=None ):
@@ -60,7 +135,7 @@ class WorkflowController( BaseController ):
stored.deleted = True
stored.flush()
# Display the management page
- return self.index( trans )
+ return trans.response.send_redirect( url_for( controller='workflow', action='index' ) )
@web.expose
@web.require_login( "edit workflows" )
@@ -348,7 +423,12 @@ class WorkflowController( BaseController ):
@web.expose
def run( self, trans, id, **kwargs ):
- stored = get_stored_workflow( trans, id )
+ stored = get_stored_workflow( trans, id, check_ownership=False )
+ user = trans.get_user()
+ if stored.user != user:
+ if trans.sa_session.query( model.StoredWorkflowUserShareAssociation ) \
+ .filter_by( user=user, stored_workflow=stored ).count() == 0:
+ error( "Workflow is not owned by or shared with current user" )
# Get the latest revision
workflow = stored.latest_workflow
# It is possible for a workflow to have 0 steps
@@ -392,7 +472,7 @@ class WorkflowController( BaseController ):
step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections )
if not errors:
# Run each step, connecting outputs to inputs
- outputs = {}
+ outputs = odict()
for step in workflow.steps:
if step.type == 'tool' or step.type is None:
tool = trans.app.toolbox.tools_by_id[ step.tool_id ]
@@ -453,7 +533,7 @@ class WorkflowController( BaseController ):
## ---- Utility methods -------------------------------------------------------
-def get_stored_workflow( trans, id ):
+def get_stored_workflow( trans, id, check_ownership=True ):
"""
Get a StoredWorkflow from the database by id, verifying ownership.
"""
@@ -466,7 +546,7 @@ def get_stored_workflow( trans, id ):
user = trans.get_user()
if not user:
error( "Must be logged in to use workflows" )
- if not( stored.user == user ):
+ if check_ownership and not( stored.user == user ):
error( "Workflow is not owned by current user" )
# Looks good
return stored
diff --git a/templates/workflow/index.mako b/templates/workflow/index.mako
index 71fe19bbe7f..686e80343c2 100644
--- a/templates/workflow/index.mako
+++ b/templates/workflow/index.mako
@@ -47,26 +47,63 @@
-
Stored workflows
+ Your workflows
%if workflows:
-
+
%for workflow in workflows:
| ${workflow.name} |
- ${len(workflow.latest_workflow.steps)}
- | ${str(workflow.update_time)[:19]} |
+ ${len(workflow.latest_workflow.steps)} |
+ ## ${str(workflow.update_time)[:19]} |
run
| edit
+ | rename
+ | share
| delete
+ |
%endfor
%else:
- You have no stored workflows.
+ You have no workflows.
+
+ %endif
+
+ Workflows shared with you by others
+
+ %if shared_by_others:
+
+
+ %for association in shared_by_others:
+ <% workflow = association.stored_workflow %>
+
+ | ${workflow.name} |
+ ${workflow.user.email} |
+ ${len(workflow.latest_workflow.steps)} |
+
+ run
+ clone
+ |
+
+ %endfor
+
+ %else:
+
+ No workflows have been shared with you.
%endif
diff --git a/templates/workflow/rename.mako b/templates/workflow/rename.mako
new file mode 100644
index 00000000000..62c9d709e5d
--- /dev/null
+++ b/templates/workflow/rename.mako
@@ -0,0 +1,36 @@
+<%inherit file="/base.mako"/>
+
+%if message:
+<%
+if messagetype is UNDEFINED:
+ mt = "done"
+else:
+ mt = messagetype
+%>
+
+
+ ${message}
+
+
+%endif
+
+
+
\ No newline at end of file
diff --git a/templates/workflow/share.mako b/templates/workflow/share.mako
new file mode 100644
index 00000000000..3b7b4848c5a
--- /dev/null
+++ b/templates/workflow/share.mako
@@ -0,0 +1,36 @@
+<%inherit file="/base.mako"/>
+
+%if message:
+<%
+if messagetype is UNDEFINED:
+ mt = "done"
+else:
+ mt = messagetype
+%>
+
+
+ ${message}
+
+
+%endif
+
+
+
\ No newline at end of file