details:
http://www.bx.psu.edu/hg/galaxy/rev/a7deb5c197ad
changeset: 3226:a7deb5c197ad
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Tue Jan 12 11:37:45 2010 -0500
description:
Enhancements to personal and public grids for pages, published histories, and workflows.
Specific improvements: (a) added community tags to grids for published pages and published
histories; (b) bug fixes for grids for published pages and published histories; © added
individual tags for pages and workflows; (d) added user grid for stored/saved workflows
(not yet included in default workflow view); (e) added grid for publicly available
workflows; (f) added pretty URLs for public workflows and ability to view a public
workflow. Fixes #232
diffstat:
lib/galaxy/model/__init__.py | 1 +
lib/galaxy/model/mapping.py | 1 +
lib/galaxy/model/migrate/versions/0032_stored_workflow_slug_column.py | 38 +
lib/galaxy/tags/tag_handler.py | 23 +-
lib/galaxy/web/base/controller.py | 18 +-
lib/galaxy/web/buildapp.py | 1 +
lib/galaxy/web/controllers/history.py | 46 +-
lib/galaxy/web/controllers/page.py | 24 +-
lib/galaxy/web/controllers/tag.py | 18 +-
lib/galaxy/web/controllers/workflow.py | 138 ++++++-
lib/galaxy/web/framework/__init__.py | 6 +
static/june_2007_style/autocomplete_tagging.css.tmpl | 6 +-
static/june_2007_style/blue/autocomplete_tagging.css | 3 +-
templates/history/list_public.mako | 2 +-
templates/history/view.mako | 136 +++---
templates/page/display.mako | 2 -
templates/page/editor.mako | 7 +-
templates/root/history.mako | 21 +-
templates/tagging_common.mako | 12 +-
templates/workflow/display.mako | 195 ++++++++++
templates/workflow/list_public.mako | 23 +
templates/workflow/sharing.mako | 7 +
22 files changed, 566 insertions(+), 162 deletions(-)
diffs (1192 lines):
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/model/__init__.py Tue Jan 12 11:37:45 2010 -0500
@@ -1114,6 +1114,7 @@
self.id = None
self.user = None
self.name = None
+ self.slug = None
self.latest_workflow_id = None
self.workflows = []
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/model/mapping.py Tue Jan 12 11:37:45 2010 -0500
@@ -513,6 +513,7 @@
Column( "name", TEXT ),
Column( "deleted", Boolean, default=False ),
Column( "importable", Boolean, default=False ),
+ Column( "slug", TEXT, index=True )
)
Workflow.table = Table( "workflow", metadata,
diff -r 56efe838b9af -r a7deb5c197ad
lib/galaxy/model/migrate/versions/0032_stored_workflow_slug_column.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/model/migrate/versions/0032_stored_workflow_slug_column.py Tue Jan 12
11:37:45 2010 -0500
@@ -0,0 +1,38 @@
+"""
+Migration script to add slug column for stored workflow.
+"""
+
+from sqlalchemy import *
+from migrate import *
+from migrate.changeset import *
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+
+def upgrade():
+
+ print __doc__
+ metadata.reflect()
+
+ StoredWorkflow_table = Table( "stored_workflow", metadata, autoload=True )
+
+ # Create slug column.
+ c = Column( "slug", TEXT, index=True )
+ c.create( StoredWorkflow_table )
+ assert c is StoredWorkflow_table.c.slug
+
+ # Create slug index.
+ try:
+ i = Index( "ix_stored_workflow_slug", StoredWorkflow_table.c.slug )
+ i.create()
+ except:
+ # Mysql doesn't have a named index, but alter should work
+ StoredWorkflow_table.c.slug.alter( unique=False )
+
+def downgrade():
+ metadata.reflect()
+
+ StoredWorkflow_table = Table( "stored_workflow", metadata, autoload=True )
+ StoredWorkflow_table.c.slug.drop()
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/tags/tag_handler.py
--- a/lib/galaxy/tags/tag_handler.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/tags/tag_handler.py Tue Jan 12 11:37:45 2010 -0500
@@ -1,9 +1,7 @@
-from galaxy.model import Tag
+from galaxy import model
import re
from sqlalchemy.sql.expression import func, and_
from sqlalchemy.sql import select
-from galaxy.model import History, HistoryTagAssociation, Dataset, DatasetTagAssociation,
\
- HistoryDatasetAssociation, HistoryDatasetAssociationTagAssociation, Page,
PageTagAssociation
class TagHandler( object ):
@@ -31,10 +29,11 @@
# Initialize with known classes.
item_tag_assoc_info = {}
- item_tag_assoc_info["History"] = ItemTagAssocInfo( History,
HistoryTagAssociation, HistoryTagAssociation.table.c.history_id )
+ item_tag_assoc_info["History"] = ItemTagAssocInfo( model.History,
model.HistoryTagAssociation, model.HistoryTagAssociation.table.c.history_id )
item_tag_assoc_info["HistoryDatasetAssociation"] = \
- ItemTagAssocInfo( HistoryDatasetAssociation,
HistoryDatasetAssociationTagAssociation,
HistoryDatasetAssociationTagAssociation.table.c.history_dataset_association_id )
- item_tag_assoc_info["Page"] = ItemTagAssocInfo( Page, PageTagAssociation,
PageTagAssociation.table.c.page_id )
+ ItemTagAssocInfo( model.HistoryDatasetAssociation,
model.HistoryDatasetAssociationTagAssociation,
model.HistoryDatasetAssociationTagAssociation.table.c.history_dataset_association_id )
+ item_tag_assoc_info["Page"] = ItemTagAssocInfo( model.Page,
model.PageTagAssociation, model.PageTagAssociation.table.c.page_id )
+ item_tag_assoc_info["StoredWorkflow"] = ItemTagAssocInfo(
model.StoredWorkflow, model.StoredWorkflowTagAssociation,
model.StoredWorkflowTagAssociation.table.c.stored_workflow_id )
def get_tag_assoc_class(self, item_class):
""" Returns tag association class for item class.
"""
@@ -55,7 +54,7 @@
# Build select statement.
cols_to_select = [ item_tag_assoc_class.table.c.tag_id, func.count('*') ]
- from_obj = item_tag_assoc_class.table.join(item_class.table).join(Tag.table)
+ from_obj = item_tag_assoc_class.table.join( item_class.table ).join(
model.Tag.table )
where_clause = ( self.get_id_col_in_item_tag_assoc_table(item_class) == item.id
)
order_by = [ func.count("*").desc() ]
group_by = item_tag_assoc_class.table.c.tag_id
@@ -101,7 +100,7 @@
# Get tag name.
if isinstance(tag, basestring):
tag_name = tag
- elif isinstance(tag, Tag):
+ elif isinstance(tag, model.Tag):
tag_name = tag.name
# Check for an item-tag association to see if item has a given tag.
@@ -168,12 +167,12 @@
def get_tag_by_id(self, db_session, tag_id):
"""Get a Tag object from a tag id."""
- return db_session.query(Tag).filter(Tag.id==tag_id).first()
+ return db_session.query( model.Tag ).filter_by( id=tag_id) .first()
def get_tag_by_name(self, db_session, tag_name):
"""Get a Tag object from a tag name (string)."""
if tag_name:
- return db_session.query( Tag ).filter( Tag.name==tag_name.lower() ).first()
+ return db_session.query( model.Tag ).filter_by( name=tag_name.lower()
).first()
return None
def _create_tag(self, db_session, tag_str):
@@ -184,9 +183,9 @@
for sub_tag in tag_hierarchy:
# Get or create subtag.
tag_name = tag_prefix + self._scrub_tag_name(sub_tag)
- tag = db_session.query(Tag).filter(Tag.name==tag_name).first()
+ tag = db_session.query( model.Tag ).filter_by( name=tag_name).first()
if not tag:
- tag = Tag(type=0, name=tag_name)
+ tag = model.Tag(type=0, name=tag_name)
# Set tag parent.
tag.parent = parent_tag
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/web/base/controller.py Tue Jan 12 11:37:45 2010 -0500
@@ -2,7 +2,7 @@
Contains functionality needed in every web interface
"""
-import os, time, logging
+import os, time, logging, re
# Pieces of Galaxy to make global in every controller
from galaxy import config, tools, web, model, util
@@ -28,7 +28,7 @@
return dict( action='display_by_username_and_slug',
username=item.user.username, slug=item.slug )
else:
return None
-
+
class BaseController( object ):
"""
Base class for Galaxy web application controllers.
@@ -57,6 +57,20 @@
if history.user != user:
error( "History is not owned by current user" )
return history
+
+ def make_item_importable( self, sa_session, item ):
+ """ Makes item importable and sets item's slug. Does not
flush/commit changes, however. Item must have name, user, importable, and slug attributes.
"""
+ item.importable = True
+
+ # Set history slug. Slug must be unique among user's importable pages.
+ slug_base = re.sub( "\s+", "-", item.name.lower() )
+ slug = slug_base
+ count = 1
+ while sa_session.query( item.__class__ ).filter_by( user=item.user, slug=slug,
importable=True ).count() != 0:
+ # Slug taken; choose a new slug based on count. This approach can handle
numerous histories with the same name gracefully.
+ slug = '%s-%i' % ( slug_base, count )
+ count += 1
+ item.slug = slug
Root = BaseController
"""
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/web/buildapp.py
--- a/lib/galaxy/web/buildapp.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/web/buildapp.py Tue Jan 12 11:37:45 2010 -0500
@@ -81,6 +81,7 @@
webapp.add_route( '/datasets/:dataset_id/:action/:filename',
controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/u/:username/p/:slug', controller='page',
action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history',
action='display_by_username_and_slug' )
+ webapp.add_route( '/u/:username/w/:slug', controller='workflow',
action='display_by_username_and_slug' )
webapp.finalize_config()
# Wrap the webapp in some useful middleware
if kwargs.get( 'middleware', True ):
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/web/controllers/history.py
--- a/lib/galaxy/web/controllers/history.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/web/controllers/history.py Tue Jan 12 11:37:45 2010 -0500
@@ -10,19 +10,18 @@
import webhelpers, logging, operator
from datetime import datetime
from cgi import escape
-import re
log = logging.getLogger( __name__ )
# States for passing messages
SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning",
"error"
+class NameColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, history ):
+ return history.get_display_name()
+
class HistoryListGrid( grids.Grid ):
# Custom column types
- class NameColumn( grids.TextColumn ):
- def get_value(self, trans, grid, history):
- return history.get_display_name()
-
class DatasetsByStateColumn( grids.GridColumn ):
def get_value( self, trans, grid, history ):
rval = []
@@ -174,21 +173,24 @@
return query.filter( model.HistoryUserShareAssociation.user == trans.user )
class PublicHistoryListGrid( grids.Grid ):
+ class NameURLColumn( PublicURLColumn, NameColumn ):
+ pass
+
title = "Public Histories"
model_class = model.History
default_sort_key = "-update_time"
default_filter = dict( public_url="All", username="All",
tags="All" )
use_async = True
columns = [
- PublicURLColumn( "Name", key="name",
model_class=model.History, filterable="advanced"),
+ NameURLColumn( "Name", key="name", model_class=model.History,
filterable="advanced" ),
OwnerColumn( "Owner", key="username", model_class=model.User,
filterable="advanced", sortable=False ),
- grids.GridColumn( "Created", key="create_time",
format=time_ago ),
+ grids.CommunityTagsColumn( "Community Tags", "tags",
model.History, model.HistoryTagAssociation, filterable="advanced",
grid_name="PublicHistoryListGrid" ),
grids.GridColumn( "Last Updated", key="update_time",
format=time_ago )
]
columns.append(
grids.MulticolFilterColumn(
"Search",
- cols_to_filter=[ columns[0], columns[1] ],
+ cols_to_filter=[ columns[0], columns[1], columns[2] ],
key="free-text-search", visible=False, filterable="standard"
)
)
operations = []
@@ -213,7 +215,6 @@
public_list_grid = PublicHistoryListGrid()
@web.expose
- @web.require_login()
def list_public( self, trans, **kwargs ):
grid = self.public_list_grid( trans, **kwargs )
if 'async' in kwargs:
@@ -272,7 +273,7 @@
elif operation == "enable import via link":
for history in histories:
if not history.importable:
- self.make_history_importable( trans.sa_session, history )
+ self.make_item_importable( trans.sa_session, history )
elif operation == "disable import via link":
if history_ids:
histories = [ self.get_history( trans, history_id ) for
history_id in history_ids ]
@@ -432,7 +433,7 @@
importable = importable in ['True', 'true', 't',
'T'];
if history and history.importable != importable:
if importable:
- self.make_history_importable( trans.sa_session, history )
+ self.make_item_importable( trans.sa_session, history )
else:
history.importable = importable
trans.sa_session.flush()
@@ -536,8 +537,9 @@
user_owns_history = user_owns_history,
show_deleted = False )
- @web.expose
+ @web.expose
def display_by_username_and_slug( self, trans, username, slug ):
+ """ Display history based on a username and slug.
"""
session = trans.sa_session
user = session.query( model.User ).filter_by( username=username ).first()
if user is None:
@@ -545,7 +547,7 @@
history = trans.sa_session.query( model.History ).filter_by( user=user, slug=slug,
deleted=False, importable=True ).first()
if history is None:
raise web.httpexceptions.HTTPNotFound()
-
+
query = trans.sa_session.query( model.HistoryDatasetAssociation ) \
.filter( model.HistoryDatasetAssociation.history ==
history ) \
.options( eagerload( "children" ) ) \
@@ -848,7 +850,7 @@
for history in histories:
trans.sa_session.add( history )
if params.get( 'enable_import_via_link', False ):
- self.make_history_importable( trans.sa_session, history )
+ self.make_item_importable( trans.sa_session, history )
trans.sa_session.flush()
elif params.get( 'disable_import_via_link', False ):
history.importable = False
@@ -964,18 +966,4 @@
msg = 'Clone with name "%s" is now included in your previously
stored histories.' % new_history.name
else:
msg = '%d cloned histories are now included in your previously stored
histories.' % len( histories )
- return trans.show_ok_message( msg )
-
- def make_history_importable( self, sa_session, history ):
- """ Makes history importable and sets history's slug. Does not
flush/commit changes, however. """
- history.importable = True
-
- # Set history slug. Slug must be unique among user's importable pages.
- slug_base = re.sub( "\s+", "-", history.name.lower() )
- slug = slug_base
- count = 1
- while sa_session.query( model.History ).filter_by( user=history.user, slug=slug,
importable=True ).count() != 0:
- # Slug taken; choose a new slug based on count. This approach can handle
numerous histories with the same name gracefully.
- slug = '%s-%i' % ( slug_base, count )
- count += 1
- history.slug = slug
\ No newline at end of file
+ return trans.show_ok_message( msg )
\ No newline at end of file
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/web/controllers/page.py
--- a/lib/galaxy/web/controllers/page.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/web/controllers/page.py Tue Jan 12 11:37:45 2010 -0500
@@ -14,19 +14,31 @@
return ""
class PageListGrid( grids.Grid ):
+ # Custom column.
+ class URLColumn( PublicURLColumn ):
+ def get_value( self, trans, grid, item ):
+ return url_for( action='display_by_username_and_slug',
username=item.user.username, slug=item.slug )
+
# Grid definition
use_panels = True
title = "Pages"
model_class = model.Page
- default_filter = { "published" : "All"}
+ default_filter = { "published" : "All", "tags" :
"All", "title" : "All"}
default_sort_key = "-create_time"
columns = [
- grids.TextColumn( "Title", key="title",
model_class=model.Page, attach_popup=True, filterable="standard" ),
- PublicURLColumn( "Public URL" ),
- grids.GridColumn( "Published", key="published",
format=format_bool, filterable="standard" ),
+ grids.TextColumn( "Title", key="title",
model_class=model.Page, attach_popup=True, filterable="advanced" ),
+ URLColumn( "Public URL" ),
+ grids.IndividualTagsColumn( "Tags", "tags", model.Page,
model.PageTagAssociation, filterable="advanced",
grid_name="PageListGrid" ),
+ grids.GridColumn( "Published", key="published",
format=format_bool, filterable="advanced" ),
grids.GridColumn( "Created", key="create_time",
format=time_ago ),
grids.GridColumn( "Last Updated", key="update_time",
format=time_ago ),
]
+ columns.append(
+ grids.MulticolFilterColumn(
+ "Search",
+ cols_to_filter=[ columns[0], columns[2] ],
+ key="free-text-search", visible=False, filterable="standard"
)
+ )
global_actions = [
grids.GridAction( "Add new page", dict( action='create' ) )
]
@@ -52,13 +64,13 @@
columns = [
PublicURLColumn( "Title", key="title",
model_class=model.Page, filterable="advanced"),
OwnerColumn( "Owner", key="username", model_class=model.User,
filterable="advanced", sortable=False ),
- grids.GridColumn( "Created", key="create_time",
format=time_ago ),
+ grids.CommunityTagsColumn( "Community Tags", "tags",
model.Page, model.PageTagAssociation, filterable="advanced",
grid_name="PageAllPublishedGrid" ),
grids.GridColumn( "Last Updated", key="update_time",
format=time_ago )
]
columns.append(
grids.MulticolFilterColumn(
"Search",
- cols_to_filter=[ columns[0], columns[1] ],
+ cols_to_filter=[ columns[0], columns[1], columns[2] ],
key="free-text-search", visible=False, filterable="standard"
)
)
def build_initial_query( self, session ):
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/web/controllers/tag.py
--- a/lib/galaxy/web/controllers/tag.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/web/controllers/tag.py Tue Jan 12 11:37:45 2010 -0500
@@ -2,8 +2,6 @@
Tags Controller: handles tagging/untagging of entities and provides autocomplete
support.
"""
-from galaxy.model import History, HistoryTagAssociation, Dataset, DatasetTagAssociation,
\
- HistoryDatasetAssociation, HistoryDatasetAssociationTagAssociation, Page,
PageTagAssociation
from galaxy.web.base.controller import *
from galaxy.tags.tag_handler import *
from sqlalchemy.sql.expression import func, and_
@@ -75,11 +73,13 @@
# Get item class. TODO: we should have a mapper that goes from class_name to
class object.
if item_class == 'History':
- item_class = History
+ item_class = model.History
elif item_class == 'HistoryDatasetAssociation':
- item_class = HistoryDatasetAssociation
+ item_class = model.HistoryDatasetAssociation
elif item_class == 'Page':
- item_class = Page
+ item_class = model.Page
+ elif item_class == 'StoredWorkflow':
+ item_class = model.StoredWorkflow
q = q.encode('utf-8')
if q.find(":") == -1:
@@ -104,9 +104,9 @@
# Build select statement.
cols_to_select = [ item_tag_assoc_class.table.c.tag_id, func.count('*') ]
- from_obj = item_tag_assoc_class.table.join(item_class.table).join(Tag.table)
+ from_obj = item_tag_assoc_class.table.join( item_class.table ).join(
model.Tag.table )
where_clause = and_(
- Tag.table.c.name.like(q + "%"),
+ model.Tag.table.c.name.like(q + "%"),
item_tag_assoc_class.table.c.user_id == user.id
)
order_by = [ func.count("*").desc() ]
@@ -155,9 +155,9 @@
# Build select statement.
cols_to_select = [ item_tag_assoc_class.table.c.value, func.count('*') ]
- from_obj = item_tag_assoc_class.table.join(item_class.table).join(Tag.table)
+ from_obj = item_tag_assoc_class.table.join( item_class.table ).join(
model.Tag.table )
where_clause = and_( item_tag_assoc_class.table.c.user_id == user.id,
- Tag.table.c.id==tag.id,
+ model.Tag.table.c.id==tag.id,
item_tag_assoc_class.table.c.value.like(tag_value +
"%") )
order_by = [ func.count("*").desc(),
item_tag_assoc_class.table.c.value ]
group_by = item_tag_assoc_class.table.c.value
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/web/controllers/workflow.py
--- a/lib/galaxy/web/controllers/workflow.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/web/controllers/workflow.py Tue Jan 12 11:37:45 2010 -0500
@@ -4,6 +4,7 @@
pkg_resources.require( "simplejson" )
import simplejson
+from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.tools.parameters import *
from galaxy.tools import DefaultToolState
from galaxy.tools.parameters.grouping import Repeat, Conditional
@@ -15,11 +16,89 @@
from galaxy.model.mapping import desc
from galaxy.model.orm import *
+class StoredWorkflowListGrid( grids.Grid ):
+ class StepsColumn( grids.GridColumn ):
+ def get_value(self, trans, grid, workflow):
+ return len( workflow.latest_workflow.steps )
+
+ # Grid definition
+ use_panels = True
+ title = "Saved Workflows"
+ model_class = model.StoredWorkflow
+ default_filter = { "name" : "All", "tags":
"All" }
+ default_sort_key = "-update_time"
+ columns = [
+ grids.TextColumn( "Name", key="name",
model_class=model.StoredWorkflow, attach_popup=True, filterable="advanced" ),
+ grids.IndividualTagsColumn( "Tags", "tags",
model.StoredWorkflow, model.StoredWorkflowTagAssociation, filterable="advanced",
grid_name="StoredWorkflowListGrid" ),
+ StepsColumn( "Steps" ),
+ grids.GridColumn( "Created", key="create_time",
format=time_ago ),
+ grids.GridColumn( "Last Updated", key="update_time",
format=time_ago ),
+ ]
+ columns.append(
+ grids.MulticolFilterColumn(
+ "Search",
+ cols_to_filter=[ columns[0], columns[1] ],
+ key="free-text-search", visible=False, filterable="standard"
)
+ )
+ operations = [
+ grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda
item: not item.deleted ), async_compatible=False ),
+ grids.GridOperation( "Run", condition=( lambda item: not item.deleted
), async_compatible=False ),
+ grids.GridOperation( "Clone", condition=( lambda item: not item.deleted
), async_compatible=False ),
+ grids.GridOperation( "Rename", condition=( lambda item: not
item.deleted ), async_compatible=False ),
+ grids.GridOperation( "Sharing", condition=( lambda item: not
item.deleted ), async_compatible=False ),
+ grids.GridOperation( "Delete", condition=( lambda item: item.deleted ),
async_compatible=True ),
+ ]
+ def apply_default_filter( self, trans, query, **kwargs ):
+ return query.filter_by( user=trans.user, deleted=False )
+
+class PublicStoredWorkflowListGrid( grids.Grid ):
+ title = "Public Workflows"
+ model_class = model.StoredWorkflow
+ default_sort_key = "-update_time"
+ default_filter = dict( public_url="All", username="All",
tags="All" )
+ use_async = True
+ columns = [
+ PublicURLColumn( "Name", key="name",
model_class=model.StoredWorkflow, filterable="advanced" ),
+ OwnerColumn( "Owner", key="username", model_class=model.User,
filterable="advanced", sortable=False ),
+ grids.CommunityTagsColumn( "Community Tags", "tags",
model.StoredWorkflow, model.StoredWorkflowTagAssociation, filterable="advanced",
grid_name="PublicWorkflowListGrid" ),
+ grids.GridColumn( "Last Updated", key="update_time",
format=time_ago )
+ ]
+ columns.append(
+ grids.MulticolFilterColumn(
+ "Search",
+ cols_to_filter=[ columns[0], columns[1], columns[2] ],
+ key="free-text-search", visible=False, filterable="standard"
)
+ )
+ operations = []
+ def build_initial_query( self, session ):
+ # Join so that searching stored_workflow.user makes sense.
+ return session.query( self.model_class ).join( model.User.table )
+ def apply_default_filter( self, trans, query, **kwargs ):
+ # A public workflow is importable, has a slug, and is not deleted.
+ return query.filter( self.model_class.importable==True ).filter(
self.model_class.slug != None ).filter( self.model_class.deleted == False )
+
class WorkflowController( BaseController ):
+ stored_list_grid = StoredWorkflowListGrid()
+ public_list_grid = PublicStoredWorkflowListGrid()
@web.expose
def index( self, trans ):
return trans.fill_template( "workflow/index.mako" )
+
+ @web.expose
+ @web.require_login( "use Galaxy workflows" )
+ def list_grid( self, trans, **kwargs ):
+ """ List user's stored workflows. """
+ status = message = None
+ if 'operation' in kwargs:
+ operation = kwargs['operation'].lower()
+ print operation
+ if operation == "rename":
+ return self.rename( trans, **kwargs )
+ history_ids = util.listify( kwargs.get( 'id', [] ) )
+ if operation == "sharing":
+ return self.sharing( trans, id=history_ids )
+ return self.stored_list_grid( trans, **kwargs )
@web.expose
@web.require_login( "use Galaxy workflows" )
@@ -64,7 +143,52 @@
return trans.fill_template( "workflow/list_for_run.mako",
workflows = workflows,
shared_by_others = shared_by_others )
-
+
+ @web.expose
+ def list_public( self, trans, **kwargs ):
+ grid = self.public_list_grid( trans, **kwargs )
+ if 'async' in kwargs:
+ return grid
+ else:
+ # Render grid wrapped in panels
+ return trans.fill_template( "workflow/list_public.mako", grid=grid
)
+
+ @web.expose
+ def display_by_username_and_slug( self, trans, username, slug ):
+ """ View workflow based on a username and slug. """
+ session = trans.sa_session
+
+ # Get and verify user and stored workflow.
+ user = session.query( model.User ).filter_by( username=username ).first()
+ if user is None:
+ raise web.httpexceptions.HTTPNotFound()
+ stored_workflow = trans.sa_session.query( model.StoredWorkflow ).filter_by(
user=user, slug=slug, deleted=False, importable=True ).first()
+ if stored_workflow is None:
+ raise web.httpexceptions.HTTPNotFound()
+
+ # Get data for workflow's steps.
+ for step in stored_workflow.latest_workflow.steps:
+ if step.type == 'tool' or step.type is None:
+ # Restore the tool state for the step
+ module = module_factory.from_workflow_step( trans, step )
+ # Any connected input needs to have value DummyDataset (these
+ # are not persisted so we need to do it every time)
+ module.add_dummy_datasets( connections=step.input_connections )
+ # Store state with the step
+ step.module = module
+ step.state = module.state
+ # Error dict
+ if step.tool_errors:
+ errors[step.id] = step.tool_errors
+ else:
+ ## Non-tool specific stuff?
+ step.module = module_factory.from_workflow_step( trans, step )
+ step.state = step.module.get_runtime_state()
+ # Connections by input name
+ step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in
step.input_connections )
+
+ return trans.fill_template_mako( "workflow/display.mako", workflow =
stored_workflow, steps = stored_workflow.latest_workflow.steps )
+
@web.expose
@web.require_login( "use Galaxy workflows" )
def share( self, trans, id, email="" ):
@@ -108,7 +232,7 @@
stored = get_stored_workflow( trans, id )
session.add( stored )
if 'enable_import_via_link' in kwargs:
- stored.importable = True
+ self.make_item_importable( trans.sa_session, stored )
session.flush()
elif 'disable_import_via_link' in kwargs:
stored.importable = False
@@ -150,15 +274,19 @@
@web.expose
@web.require_login( "use Galaxy workflows" )
- def rename( self, trans, id, new_name=None ):
+ def rename( self, trans, id, new_name=None, **kwargs ):
stored = get_stored_workflow( trans, id )
if new_name is not None:
stored.name = new_name
trans.sa_session.flush()
- trans.set_message( "Workflow renamed to '%s'." % new_name
)
+ # For current workflows grid:
+ trans.set_message ( "Workflow renamed to '%s'." % new_name
)
return self.list( trans )
+ # For new workflows grid:
+ #message = "Workflow renamed to '%s'." % new_name
+ #return self.list_grid( trans, message=message, status='done' )
else:
- return form( url_for( id=trans.security.encode_id(stored.id) ), "Rename
workflow", submit_text="Rename" ) \
+ return form( url_for( action='rename',
id=trans.security.encode_id(stored.id) ), "Rename workflow",
submit_text="Rename" ) \
.add_text( "new_name", "Workflow Name",
value=stored.name )
@web.expose
diff -r 56efe838b9af -r a7deb5c197ad lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py Mon Jan 11 17:04:45 2010 -0500
+++ b/lib/galaxy/web/framework/__init__.py Tue Jan 12 11:37:45 2010 -0500
@@ -553,6 +553,12 @@
context.
"""
self.template_context['message'] = message
+ def get_message( self ):
+ """
+ Convenience method for getting the 'message' element of the template
+ context.
+ """
+ return self.template_context['message']
def show_message( self, message, type='info', refresh_frames=[], cont=None
):
"""
Convenience method for displaying a simple page with a single message.
diff -r 56efe838b9af -r a7deb5c197ad static/june_2007_style/autocomplete_tagging.css.tmpl
--- a/static/june_2007_style/autocomplete_tagging.css.tmpl Mon Jan 11 17:04:45 2010 -0500
+++ b/static/june_2007_style/autocomplete_tagging.css.tmpl Tue Jan 12 11:37:45 2010 -0500
@@ -67,7 +67,11 @@
.tag-area {
width: 100%;
cursor: pointer;
- border: solid 1px #eee;
+}
+
+.individual-tags
+{
+ border:solid 1px #eee;
}
.active-tag-area {
diff -r 56efe838b9af -r a7deb5c197ad static/june_2007_style/blue/autocomplete_tagging.css
--- a/static/june_2007_style/blue/autocomplete_tagging.css Mon Jan 11 17:04:45 2010 -0500
+++ b/static/june_2007_style/blue/autocomplete_tagging.css Tue Jan 12 11:37:45 2010 -0500
@@ -6,7 +6,8 @@
.ac_even{margin-left:0.3em;}
.ac_over{background-color:#0A246A;color:white;}
.ac_header{font-style:normal;color:gray;border-bottom:0.1em solid gray;}
-.tag-area{width:100%;cursor:pointer;border:solid 1px #eee;}
+.tag-area{width:100%;cursor:pointer;}
+.individual-tags{border:solid 1px #eee;}
.active-tag-area{background-color:white;}
.toggle-link{font-weight:normal;padding:0.3em;margin-bottom:1em;width:100%;padding:0.2em
0em 0.2em 0em;}
.tag-button{width:auto;color:#444;text-decoration:none;display:inline-block;cursor:pointer;margin:0.2em;border:0;padding:0.1em
0.5em 0.1em
0.5em;-moz-border-radius:.5em;-webkit-border-radius:.5em;border-radius:.5em;background:#bbb;}
diff -r 56efe838b9af -r a7deb5c197ad templates/history/list_public.mako
--- a/templates/history/list_public.mako Mon Jan 11 17:04:45 2010 -0500
+++ b/templates/history/list_public.mako Tue Jan 12 11:37:45 2010 -0500
@@ -15,7 +15,7 @@
<div style="overflow: auto; height: 100%;">
<div class="page-container" style="padding: 10px;">
- ${grid}
+ ${unicode( grid, 'utf-8' )}
</div>
</div>
diff -r 56efe838b9af -r a7deb5c197ad templates/history/view.mako
--- a/templates/history/view.mako Mon Jan 11 17:04:45 2010 -0500
+++ b/templates/history/view.mako Tue Jan 12 11:37:45 2010 -0500
@@ -256,14 +256,11 @@
//
function community_tag_click(tag_name, tag_value)
{
- // Do nothing until community tags are implemented in public histories grid.
- /*
var href = '${h.url_for( controller='/history',
action='list_public')}';
href = href + "?f-tags=" + tag_name;
if (tag_value != null && tag_value != "")
href = href + ":" + tag_value;
self.location = href;
- */
}
</script>
</%def>
@@ -339,78 +336,79 @@
</div>
<div class="unified-panel-body">
- <div class="page-body">
- ## Render view of history.
- <div id="top-links" class="historyLinks"
style="padding: 0px 0px 5px 0px">
- %if not user_owns_history:
- <a href="${h.url_for( controller='history',
action='imp', id=trans.security.encode_id(history.id) )}">import and start
using history</a> |
+ <div style="overflow: auto; height: 100%;">
+ <div class="page-body">
+ ## Render view of history.
+ <div id="top-links" class="historyLinks"
style="padding: 0px 0px 5px 0px">
+ %if not user_owns_history:
+ <a href="${h.url_for( controller='history',
action='imp', id=trans.security.encode_id(history.id) )}">import and start
using history</a> |
+ %endif
+ <a href="${get_history_link( history
)}">${_('refresh')}</a>
+ %if show_deleted:
+ | <a href="${h.url_for('history',
show_deleted=False)}">${_('hide deleted')}</a>
+ %endif
+ </div>
+
+ <div id="history-name-area" class="historyLinks"
style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
+ %if user_owns_history:
+ <div style="float: right"><a
id="history-rename" title="Rename" class="icon-button edit"
target="galaxy_main" href="${h.url_for( controller='history',
action='rename' )}"></a></div>
+ %endif
+ <div
id="history-name">${history.get_display_name()}</div>
+ </div>
+
+ %if history.deleted:
+ <div class="warningmessagesmall">
+ ${_('You are currently viewing a deleted history!')}
+ </div>
+ <p></p>
%endif
- <a href="${get_history_link( history
)}">${_('refresh')}</a>
- %if show_deleted:
- | <a href="${h.url_for('history',
show_deleted=False)}">${_('hide deleted')}</a>
+
+ %if not datasets:
+
+ <div class="infomessagesmall"
id="emptyHistoryMessage">
+
+ %else:
+
+ ## Render requested datasets, ordered from newest to oldest
+ %for data in datasets:
+ %if data.visible:
+ <div class="historyItemContainer
visible-right-border" id="historyItemContainer-${data.id}">
+ ${render_dataset( data, data.hid, show_deleted_on_refresh
= show_deleted, user_owns_dataset=user_owns_history )}
+ </div>
+ %endif
+ %endfor
+
+ <div class="infomessagesmall"
id="emptyHistoryMessage" style="display:none;">
%endif
+ ${_("Your history is empty. Click 'Get Data' on the
left pane to start")}
+ </div>
</div>
-
- <div id="history-name-area" class="historyLinks"
style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
- %if user_owns_history:
- <div style="float: right"><a
id="history-rename" title="Rename" class="icon-button edit"
target="galaxy_main" href="${h.url_for( controller='history',
action='rename' )}"></a></div>
- %endif
- <div
id="history-name">${history.get_display_name()}</div>
- </div>
-
- %if history.deleted:
- <div class="warningmessagesmall">
- ${_('You are currently viewing a deleted history!')}
+
+ <div class="page-meta">
+ ## Histories.
+ <div><strong>Related Histories</strong></div>
+ <p>
+ <a href="${h.url_for ( controller='/history',
action='list_public' )}">All public histories</a><br>
+ <a href="${href_to_user_histories}">Histories owned
by ${history.user.username}</a>
+
+ ## Tags.
+ <div><strong>Tags</strong></div>
+ <p>
+ ## Community tags.
+ <div>
+ Community:
+ ${render_community_tagging_element( tagged_item=history,
tag_click_fn='community_tag_click', use_toggle_link=False )}
+ %if len ( history.tags ) == 0:
+ none
+ %endif
</div>
- <p></p>
- %endif
-
- %if not datasets:
-
- <div class="infomessagesmall"
id="emptyHistoryMessage">
-
- %else:
-
- ## Render requested datasets, ordered from newest to oldest
- %for data in datasets:
- %if data.visible:
- <div class="historyItemContainer
visible-right-border" id="historyItemContainer-${data.id}">
- ${render_dataset( data, data.hid, show_deleted_on_refresh =
show_deleted, user_owns_dataset=user_owns_history )}
- </div>
- %endif
- %endfor
-
- <div class="infomessagesmall"
id="emptyHistoryMessage" style="display:none;">
- %endif
- ${_("Your history is empty. Click 'Get Data' on the left
pane to start")}
+ ## Individual tags.
+ <p>
+ <div>
+ Yours:
+ ${render_individual_tagging_element( user=trans.get_user(),
tagged_item=history, elt_context='view.mako', use_toggle_link=False )}
</div>
- </div>
-
- <div class="page-meta">
- ## Histories.
- <div><strong>Related Histories</strong></div>
- <p>
- <a href="${h.url_for ( controller='/history',
action='list_public' )}">All public histories</a><br>
- <a href="${href_to_user_histories}">Histories owned by
${history.user.username}</a>
-
- ## Tags.
- <div><strong>Tags</strong></div>
- <p>
- ## Community tags.
- <div>
- Community:
- ${render_community_tagging_element( tagged_item=history,
tag_click_fn='community_tag_click', use_toggle_link=False )}
- %if len ( history.tags ) == 0:
- none
- %endif
- </div>
- ## Individual tags.
- <p>
- <div>
- Yours:
- ${render_individual_tagging_element( user=trans.get_user(),
tagged_item=history, elt_context='view.mako', use_toggle_link=False )}
</div>
</div>
-
</div>
</%def>
diff -r 56efe838b9af -r a7deb5c197ad templates/page/display.mako
--- a/templates/page/display.mako Mon Jan 11 17:04:45 2010 -0500
+++ b/templates/page/display.mako Tue Jan 12 11:37:45 2010 -0500
@@ -151,13 +151,11 @@
function community_tag_click(tag_name, tag_value)
{
// Do nothing until community tags implemented in published pages grid.
- /*
var href = '${h.url_for( controller='/page',
action='list_published')}';
href = href + "?f-tags=" + tag_name;
if (tag_value != null && tag_value != "")
href = href + ":" + tag_value;
self.location = href;
- */
}
</script>
</%def>
diff -r 56efe838b9af -r a7deb5c197ad templates/page/editor.mako
--- a/templates/page/editor.mako Mon Jan 11 17:04:45 2010 -0500
+++ b/templates/page/editor.mako Tue Jan 12 11:37:45 2010 -0500
@@ -260,7 +260,12 @@
// Build href from history info.
var href;
if (history_slug != "" &&
history_user_username != "")
- var href = "/u/" +
history_user_username + "/h/" + history_slug;
+ {
+ var href =
+ '${h.url_for(
controller='/history', action='display_by_username_and_slug',
username='USERNAME', slug='SLUG' )}';
+ href = href.replace('USERNAME',
history_user_username);
+ href = href.replace('SLUG',
history_slug);
+ }
else
var href = '${h.url_for(
controller='/history', action='view' )}?id=' + item_id;
wym.insert("<a href='" +
href + "'>History '" + history_name + "'</a>");
diff -r 56efe838b9af -r a7deb5c197ad templates/root/history.mako
--- a/templates/root/history.mako Mon Jan 11 17:04:45 2010 -0500
+++ b/templates/root/history.mako Tue Jan 12 11:37:45 2010 -0500
@@ -260,25 +260,6 @@
count++;
return count;
};
-
- //
- // Function provides text for tagging toggle link.
- //
- var get_toggle_link_text = function(tags)
- {
- var text = "";
- var num_tags = array_length(tags);
- if (num_tags != 0)
- {
- text = num_tags + (num_tags != 1 ? " Tags" : " Tag");
- }
- else
- {
- // No tags.
- text = "Add tags to history";
- }
- return text;
- };
</script>
<style>
@@ -327,7 +308,7 @@
margin-bottom: 0.5em;
}
</style>
- ${render_individual_tagging_element( user=trans.get_user(), tagged_item=history,
elt_context='history.mako', get_toggle_link_text_fn='get_toggle_link_text'
)}
+ ${render_individual_tagging_element( user=trans.get_user(), tagged_item=history,
elt_context='history.mako' )}
%endif
%if not datasets:
diff -r 56efe838b9af -r a7deb5c197ad templates/tagging_common.mako
--- a/templates/tagging_common.mako Mon Jan 11 17:04:45 2010 -0500
+++ b/templates/tagging_common.mako Tue Jan 12 11:37:45 2010 -0500
@@ -20,7 +20,7 @@
%endif
## Render HTML for a list of tags.
-<%def name="render_tagging_element_html(elt_id=None, tags=None, editable=True,
use_toggle_link=True, input_size='15', in_form=False)">
+<%def name="render_tagging_element_html(elt_id=None, tags=None, editable=True,
use_toggle_link=True, input_size='15', in_form=False,
tag_type='individual')">
## Useful attributes.
<%
num_tags = len( tags )
@@ -35,9 +35,13 @@
%endif
%if use_toggle_link:
- <a class="toggle-link" href="#">${num_tags}
Tags</a>
+ <a class="toggle-link" href="#">${num_tags}
Tag${iff( num_tags == 1, "", "s")}</a>
%endif
- <div class="tag-area">
+ <div class="tag-area
+ %if tag_type == 'individual':
+ individual-tags
+ %endif
+ ">
## Build buttons for current tags.
%for tag in tags:
@@ -88,7 +92,7 @@
elt_id = int ( floor ( random()*maxint ) )
community_tags = tag_handler.get_community_tags(trans.sa_session, tagged_item,
10)
%>
- ${self.render_tagging_element_html(elt_id=elt_id, tags=community_tags,
use_toggle_link=use_toggle_link, editable=False)}
+ ${self.render_tagging_element_html(elt_id=elt_id, tags=community_tags,
use_toggle_link=use_toggle_link, editable=False, tag_type="community")}
## Set up tag click function.
<script type="text/javascript">
diff -r 56efe838b9af -r a7deb5c197ad templates/workflow/display.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/workflow/display.mako Tue Jan 12 11:37:45 2010 -0500
@@ -0,0 +1,195 @@
+<%inherit file="/base_panels.mako"/>
+<%namespace file="/display_common.mako" import="get_history_link"
/>
+<%namespace file="../tagging_common.mako"
import="render_individual_tagging_element, render_community_tagging_element"
/>
+
+<%! from galaxy.tools.parameters import DataToolParameter %>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${h.js( "galaxy.base", "jquery", "json2",
"jquery.autocomplete", "autocomplete_tagging" )}
+
+ <script type="text/javascript">
+ //
+ // Handle click on community tag.
+ //
+ function community_tag_click(tag_name, tag_value)
+ {
+ var href = '${h.url_for( controller='/workflow',
action='list_public')}';
+ href = href + "?f-tags=" + tag_name;
+ if (tag_value != null && tag_value != "")
+ href = href + ":" + tag_value;
+ self.location = href;
+ }
+ </script>
+</%def>
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "workflow", "autocomplete_tagging" )}
+ <style type="text/css">
+ .page-body
+ {
+ padding: 10px;
+ float: left;
+ width: 65%;
+ }
+ .page-meta
+ {
+ float: right;
+ width: 27%;
+ padding: 0.5em;
+ margin: 0.25em;
+ vertical-align: text-top;
+ border: 2px solid #DDDDDD;
+ border-top: 4px solid #DDDDDD;
+ }
+ div.toolForm{
+ margin-top: 10px;
+ margin-bottom: 10px;
+ }
+ </style>
+ <noscript>
+ <style>
+ .historyItemBody {
+ display: block;
+ }
+ </style>
+ </noscript>
+</%def>
+
+<%def name="init()">
+<%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.message_box_visible=False
+%>
+</%def>
+
+<%def name="do_inputs( inputs, values, prefix, step, other_values=None
)">
+ %for input_index, input in enumerate( inputs.itervalues() ):
+ %if input.type == "repeat":
+ <div class="repeat-group">
+ <div
class="form-title-row"><b>${input.title_plural}</b></div>
+ <% repeat_values = values[input.name] %>
+ %for i in range( len( repeat_values ) ):
+ <div class="repeat-group-item">
+ <% index = repeat_values[i]['__index__'] %>
+ <div class="form-title-row"><b>${input.title} ${i +
1}</b></div>
+ ${do_inputs( input.inputs, repeat_values[ i ], prefix + input.name +
"_" + str(index) + "|", step, other_values )}
+
+ </div>
+ %endfor
+ </div>
+ %elif input.type == "conditional":
+ <% group_values = values[input.name] %>
+ <% current_case = group_values['__current_case__'] %>
+ <% new_prefix = prefix + input.name + "|" %>
+ ${row_for_param( input.test_param, group_values[ input.test_param.name ],
other_values, prefix, step )}
+ ${do_inputs( input.cases[ current_case ].inputs, group_values, new_prefix, step,
other_values )}
+ %else:
+ ${row_for_param( input, values[ input.name ], other_values, prefix, step )}
+ %endif
+ %endfor
+</%def>
+
+<%def name="row_for_param( param, value, other_values, prefix, step )">
+ <% cls = "form-row" %>
+ <div class="${cls}">
+ <label>${param.get_label()}</label>
+ <div>
+ %if isinstance( param, DataToolParameter ):
+ %if ( prefix + param.name ) in step.input_connections_by_name:
+ <%
+ conn = step.input_connections_by_name[ prefix + param.name ]
+ %>
+ Output dataset '${conn.output_name}' from step
${int(conn.output_step.order_index)+1}
+ %else:
+ ## FIXME: Initialize in the controller
+ <%
+ if value is None:
+ value = other_values[ param.name ] = param.get_initial_value( t,
other_values )
+ %>
+ ${param.get_html_field( t, value, other_values ).get_html(
str(step.id) + "|" + prefix )}
+ %endif
+ %else:
+ ${param.value_to_display_text( value, app )}
+ %endif
+ </div>
+ </div>
+</%def>
+
+<%def name="center_panel()">
+ ## Get URL to other workflows owned by user that owns this workflow.
+ <%
+ ##TODO: is there a better way to create this URL? Can't use
'f-username' as a key b/c it's not a valid identifier.
+ href_to_user_workflows = h.url_for( action='list_public',
xxx=workflow.user.username )
+ href_to_user_workflows = href_to_user_workflows.replace( 'xxx',
'f-username' )
+ %>
+
+ <div class="unified-panel-header" unselectable="on">
+ <div class="unified-panel-header-inner">
+ <a href="${h.url_for ( action='list_public'
)}">Public Workflows</a> |
+ <a
href="${href_to_user_workflows}">${workflow.user.username}</a> |
${workflow.name}
+ </div>
+ </div>
+
+ <div class="unified-panel-body">
+ <div style="overflow: auto; height: 100%;">
+ <div class="page-body">
+ ## Render top links.
+ <div id="top-links" style="padding: 0px 0px 5px
0px">
+ %if workflow.user != trans.get_user():
+ <a href="${h.url_for( action='imp',
id=trans.security.encode_id(workflow.id) )}">import and start using
workflow</a>
+ %endif
+ </div>
+
+ ## Render Workflow.
+ <h2>${workflow.name}</h2>
+ %for i, step in enumerate( steps ):
+ %if step.type == 'tool' or step.type is None:
+ <% tool = app.toolbox.tools_by_id[step.tool_id] %>
+ <div class="toolForm">
+ <div class="toolFormTitle">Step
${int(step.order_index)+1}: ${tool.name}</div>
+ <div class="toolFormBody">
+ ${do_inputs( tool.inputs, step.state.inputs, "",
step )}
+ </div>
+ </div>
+ %else:
+ <% module = step.module %>
+ <div class="toolForm">
+ <div class="toolFormTitle">Step
${int(step.order_index)+1}: ${module.name}</div>
+ <div class="toolFormBody">
+ </div>
+ </div>
+ %endif
+ %endfor
+ </div>
+
+ <div class="page-meta">
+ ## Workflows.
+ <div><strong>Related Workflows</strong></div>
+ <p>
+ <a href="${h.url_for ( action='list_public'
)}">All public workflows</a><br>
+ <a href="${href_to_user_workflows}">Workflows owned
by ${workflow.user.username}</a>
+
+ ## Tags.
+ <div><strong>Tags</strong></div>
+ <p>
+ ## Community tags.
+ <div>
+ Community:
+ ${render_community_tagging_element( tagged_item=workflow,
tag_click_fn='community_tag_click', use_toggle_link=False )}
+ %if len ( workflow.tags ) == 0:
+ none
+ %endif
+ </div>
+ ## Individual tags.
+ <p>
+ <div>
+ Yours:
+ ${render_individual_tagging_element( user=trans.get_user(),
tagged_item=workflow, elt_context='view.mako', use_toggle_link=False )}
+ </div>
+ </div>
+ </div>
+ </div>
+</%def>
diff -r 56efe838b9af -r a7deb5c197ad templates/workflow/list_public.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/workflow/list_public.mako Tue Jan 12 11:37:45 2010 -0500
@@ -0,0 +1,23 @@
+<%inherit file="/base_panels.mako"/>
+
+<%def name="init()">
+<%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.active_view="page"
+ self.message_box_visible=False
+%>
+</%def>
+
+<%def name="center_panel()">
+
+ ## <iframe name="galaxy_main" id="galaxy_main"
frameborder="0" style="position: absolute; width: 100%; height: 100%;"
src="${h.url_for( controller="page", action="list" )}">
</iframe>
+
+ <div style="overflow: auto; height: 100%;">
+ <div class="page-container" style="padding: 10px;">
+ ${unicode( grid, 'utf-8' )}
+ </div>
+ </div>
+
+
+</%def>
diff -r 56efe838b9af -r a7deb5c197ad templates/workflow/sharing.mako
--- a/templates/workflow/sharing.mako Mon Jan 11 17:04:45 2010 -0500
+++ b/templates/workflow/sharing.mako Tue Jan 12 11:37:45 2010 -0500
@@ -4,7 +4,14 @@
<p>
%if stored.importable:
+ <p>
+ Anyone can view this workflow by visiting the following URL:
+ <% url = h.url_for( action='display_by_username_and_slug',
username=trans.get_user().username, slug=stored.slug, qualified=True ) %>
+ <blockquote>
+ <a href="${url}">${url}</a>
+ </blockquote>
+ <p>
Anyone can import this workflow into their history via the following URL:
<% url = h.url_for( action='imp',
id=trans.security.encode_id(stored.id), qualified=True ) %>
<blockquote>