details: http://www.bx.psu.edu/hg/galaxy/rev/fe6e3c197d69 changeset: 3263:fe6e3c197d69 user: jeremy goecks <jeremy.goecks@emory.edu> date: Sun Jan 24 17:38:05 2010 -0500 description: Added sharing options to pages and refactored sharing/display code to create a common code base. Also small additions to grid framework and bug fixes for pages. diffstat: lib/galaxy/model/__init__.py | 5 + lib/galaxy/model/mapping.py | 11 + lib/galaxy/model/migrate/versions/0034_page_user_share_association.py | 41 + lib/galaxy/web/base/controller.py | 6 +- lib/galaxy/web/controllers/history.py | 13 +- lib/galaxy/web/controllers/page.py | 131 +++- lib/galaxy/web/controllers/workflow.py | 5 +- templates/base_panels.mako | 2 +- templates/display_base.mako | 69 +- templates/display_common.mako | 91 ++ templates/grid_base.mako | 77 +- templates/grid_base_async.mako | 4 +- templates/grid_common.mako | 8 +- templates/history/view.mako | 2 +- templates/page/display.mako | 5 - templates/page/editor.mako | 8 +- templates/page/index.mako | 33 +- templates/page/select_histories_grid.mako | 4 +- templates/share_base.mako | 36 + templates/sharing_base.mako | 317 ++++----- templates/workflow/display.mako | 2 +- 21 files changed, 552 insertions(+), 318 deletions(-) diffs (1304 lines): diff -r a72dc4023e59 -r fe6e3c197d69 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py Sat Jan 23 15:52:42 2010 -0500 +++ b/lib/galaxy/model/__init__.py Sun Jan 24 17:38:05 2010 -0500 @@ -1452,6 +1452,11 @@ self.user = None self.title = None self.content = None + +class PageUserShareAssociation( object ): + def __init__( self ): + self.page = None + self.user = None class Visualization( object ): def __init__( self ): diff -r a72dc4023e59 -r fe6e3c197d69 lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py Sat Jan 23 15:52:42 2010 -0500 +++ b/lib/galaxy/model/mapping.py Sun Jan 24 17:38:05 2010 -0500 @@ -683,6 +683,12 @@ Column( "content", TEXT ) ) +PageUserShareAssociation.table = Table( "page_user_share_association", metadata, + Column( "id", Integer, primary_key=True ), + Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), + Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) + ) + Visualization.table = Table( "visualization", metadata, Column( "id", Integer, primary_key=True ), Column( "create_time", DateTime, default=now ), @@ -1236,6 +1242,11 @@ lazy=False ), tags=relation(PageTagAssociation, order_by=PageTagAssociation.table.c.id, backref="pages") ) ) + +assign_mapper( context, PageUserShareAssociation, PageUserShareAssociation.table, + properties=dict( user=relation( User, backref='pages_shared_by_others' ), + page=relation( Page, backref='users_shared_with' ) + ) ) assign_mapper( context, VisualizationRevision, VisualizationRevision.table ) diff -r a72dc4023e59 -r fe6e3c197d69 lib/galaxy/model/migrate/versions/0034_page_user_share_association.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/model/migrate/versions/0034_page_user_share_association.py Sun Jan 24 17:38:05 2010 -0500 @@ -0,0 +1,41 @@ +""" +Migration script to create a table for page-user share association. +""" + +from sqlalchemy import * +from sqlalchemy.orm import * +from migrate import * +from migrate.changeset import * + +import logging +log = logging.getLogger( __name__ ) + +metadata = MetaData( migrate_engine ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) + +PageUserShareAssociation_table = Table( "page_user_share_association", metadata, + Column( "id", Integer, primary_key=True ), + Column( "page_id", Integer, ForeignKey( "page.id" ), index=True ), + Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ) + ) + +def upgrade(): + print __doc__ + metadata.reflect() + + # Create stored_workflow_tag_association table. + try: + PageUserShareAssociation_table.create() + except Exception, e: + print str(e) + log.debug( "Creating page_user_share_association table failed: %s" % str( e ) ) + +def downgrade(): + metadata.reflect() + + # Drop workflow_tag_association table. + try: + PageUserShareAssociation_table.drop() + except Exception, e: + print str(e) + log.debug( "Dropping page_user_share_association table failed: %s" % str( e ) ) \ No newline at end of file diff -r a72dc4023e59 -r fe6e3c197d69 lib/galaxy/web/base/controller.py --- a/lib/galaxy/web/base/controller.py Sat Jan 23 15:52:42 2010 -0500 +++ b/lib/galaxy/web/base/controller.py Sun Jan 24 17:38:05 2010 -0500 @@ -143,9 +143,11 @@ def _make_item_accessible( self, sa_session, item ): """ Makes item accessible--viewable and importable--and sets item's slug. Does not flush/commit changes, however. Item must have name, user, importable, and slug attributes. """ item.importable = True + self.set_item_slug( sa_session, item ) - # Set item slug. Slug must be unique among user's importable items for item's class. - if item.slug is None: + def set_item_slug( self, sa_session, item ): + """ Set item slug. Slug is unique among user's importable items for item's class. """ + if item.slug is None or item.slug == "": slug_base = re.sub( "\s+", "-", item.name.lower() ) slug = slug_base count = 1 diff -r a72dc4023e59 -r fe6e3c197d69 lib/galaxy/web/controllers/history.py --- a/lib/galaxy/web/controllers/history.py Sat Jan 23 15:52:42 2010 -0500 +++ b/lib/galaxy/web/controllers/history.py Sun Jan 24 17:38:05 2010 -0500 @@ -225,7 +225,7 @@ elif operation == "enable import via link": for history in histories: if not history.importable: - self.make_item_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 ] @@ -380,16 +380,16 @@ return @web.expose - @web.require_login( "set history's importable flag" ) - def set_importable_async( self, trans, id=None, importable=False ): + @web.require_login( "set history's accessible flag" ) + def set_accessible_async( self, trans, id=None, accessible=False ): """ Set history's importable attribute and sets history's slug. """ history = self.get_history( trans, id, True ) # Only set if importable value would change; this prevents a change in the update_time unless attribute really changed. - importable = importable in ['True', 'true', 't', 'T']; + importable = accessible in ['True', 'true', 't', 'T']; if history and history.importable != importable: if importable: - self.make_item_importable( trans.sa_session, history ) + self._make_item_accessible( trans.sa_session, history ) else: history.importable = importable trans.sa_session.flush() @@ -588,7 +588,7 @@ for history in histories: trans.sa_session.add( history ) if params.get( 'enable_import_via_link', False ): - self.make_item_importable( trans.sa_session, history ) + self._make_item_accessible( trans.sa_session, history ) trans.sa_session.flush() elif params.get( 'disable_import_via_link', False ): history.importable = False @@ -893,6 +893,7 @@ share.history = history share.user = send_to_user trans.sa_session.add( share ) + self.set_item_slug( trans.sa_session, history ) trans.sa_session.flush() if history not in shared_histories: shared_histories.append( history ) diff -r a72dc4023e59 -r fe6e3c197d69 lib/galaxy/web/controllers/page.py --- a/lib/galaxy/web/controllers/page.py Sat Jan 23 15:52:42 2010 -0500 +++ b/lib/galaxy/web/controllers/page.py Sun Jan 24 17:38:05 2010 -0500 @@ -29,7 +29,7 @@ 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" ), + SharingStatusColumn( "Sharing", key="sharing", model_class=model.History, filterable="advanced", sortable=False ), grids.GridColumn( "Created", key="create_time", format=time_ago ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), ] @@ -46,9 +46,8 @@ grids.GridOperation( "View", allow_multiple=False, url_args=dict( action='display') ), grids.GridOperation( "Edit name/id", allow_multiple=False, url_args=dict( action='edit') ), grids.GridOperation( "Edit content", allow_multiple=False, url_args=dict( action='edit_content') ), + grids.GridOperation( "Share or Publish", allow_multiple=False, condition=( lambda item: not item.deleted ), async_compatible=False ), grids.GridOperation( "Delete" ), - grids.GridOperation( "Publish", condition=( lambda item: not item.published ) ), - grids.GridOperation( "Unpublish", condition=( lambda item: item.published ) ), ] def apply_default_filter( self, trans, query, **kwargs ): return query.filter_by( user=trans.user, deleted=False ) @@ -149,7 +148,7 @@ def apply_default_filter( self, trans, query, **kwargs ): return query.filter_by( user=trans.user, purged=False ) -class PageController( BaseController ): +class PageController( BaseController, Sharable ): _page_list = PageListGrid() _all_published_list = PageAllPublishedGrid() @@ -157,7 +156,8 @@ @web.expose @web.require_login() - def index( self, trans, *args, **kwargs ): + def list( self, trans, *args, **kwargs ): + """ List user's pages. """ # Handle operation if 'operation' in kwargs and 'id' in kwargs: session = trans.sa_session @@ -167,15 +167,24 @@ item = session.query( model.Page ).get( trans.security.decode_id( id ) ) if operation == "delete": item.deleted = True - elif operation == "publish": - item.published = True - elif operation == "unpublish": - item.published = False + if operation == "share or publish": + return self.sharing( trans, **kwargs ) session.flush() + # Build grid grid = self._page_list( trans, *args, **kwargs ) + + # Build list of pages shared with user. + shared_by_others = trans.sa_session \ + .query( model.PageUserShareAssociation ) \ + .filter_by( user=trans.get_user() ) \ + .join( model.Page.table ) \ + .filter( model.Page.deleted == False ) \ + .order_by( desc( model.Page.update_time ) ) \ + .all() + # Render grid wrapped in panels - return trans.fill_template( "page/index.mako", grid=grid ) + return trans.fill_template( "page/index.mako", grid=grid, shared_by_others=shared_by_others ) @web.expose def list_published( self, trans, *args, **kwargs ): @@ -222,7 +231,7 @@ session.flush() # Display the management page ## trans.set_message( "Page '%s' created" % page.title ) - return trans.response.send_redirect( web.url_for( action='index' ) ) + return trans.response.send_redirect( web.url_for( action='list' ) ) return trans.show_form( web.FormBuilder( web.url_for(), "Create new page", submit_text="Submit" ) .add_text( "page_title", "Page title", value=page_title, error=page_title_err ) @@ -288,6 +297,78 @@ return trans.fill_template( "page/editor.mako", page=page ) @web.expose + @web.require_login( "use Galaxy pages" ) + def sharing( self, trans, id, **kwargs ): + """ Handle page sharing. """ + + # Get session and page. + session = trans.sa_session + page = trans.sa_session.query( model.Page ).get( trans.security.decode_id( id ) ) + + # Do operation on page. + if 'make_accessible_via_link' in kwargs: + self._make_item_accessible( trans.sa_session, page ) + elif 'make_accessible_and_publish' in kwargs: + self._make_item_accessible( trans.sa_session, page ) + page.published = True + elif 'publish' in kwargs: + page.published = True + elif 'disable_link_access' in kwargs: + page.importable = False + elif 'unpublish' in kwargs: + page.published = False + elif 'disable_link_access_and_unpubish' in kwargs: + page.importable = page.published = False + elif 'unshare_user' in kwargs: + user = session.query( model.User ).get( trans.security.decode_id( kwargs['unshare_user' ] ) ) + if not user: + error( "User not found for provided id" ) + association = session.query( model.PageUserShareAssociation ) \ + .filter_by( user=user, page=page ).one() + session.delete( association ) + + session.flush() + + return trans.fill_template( "/sharing_base.mako", + item=page ) + + @web.expose + @web.require_login( "use Galaxy pages" ) + def share( self, trans, id, email="" ): + msg = mtype = None + page = trans.sa_session.query( model.Page ).get( trans.security.decode_id( id ) ) + if email: + other = trans.sa_session.query( model.User ) \ + .filter( and_( model.User.table.c.email==email, + model.User.table.c.deleted==False ) ) \ + .first() + if not other: + mtype = "error" + msg = ( "User '%s' does not exist" % email ) + elif other == trans.get_user(): + mtype = "error" + msg = ( "You cannot share a workflow with yourself" ) + elif trans.sa_session.query( model.PageUserShareAssociation ) \ + .filter_by( user=other, page=page ).count() > 0: + mtype = "error" + msg = ( "Workflow already shared with '%s'" % email ) + else: + share = model.PageUserShareAssociation() + share.page = page + share.user = other + session = trans.sa_session + session.add( share ) + self.set_item_slug( session, page ) + session.flush() + trans.set_message( "Page '%s' shared with user '%s'" % ( page.title, other.email ) ) + return trans.response.send_redirect( url_for( controller='page', action='sharing', id=id ) ) + return trans.fill_template( "/share_base.mako", + message = msg, + messagetype = mtype, + item=page, + email=email ) + + @web.expose @web.require_login() def save( self, trans, id, content ): id = trans.security.decode_id( id ) @@ -308,20 +389,30 @@ def display( self, trans, id ): id = trans.security.decode_id( id ) page = trans.sa_session.query( model.Page ).get( id ) - if page.user is not trans.user: - error( "Page is not owned by current user" ) - return trans.fill_template( "page/display.mako", page=page ) - + if not page: + raise web.httpexceptions.HTTPNotFound() + return self.display_by_username_and_slug( trans, page.user.username, page.slug ) + @web.expose def display_by_username_and_slug( self, trans, username, slug ): + """ Display page based on a username and slug. """ + session = trans.sa_session + + # Get page. session = trans.sa_session user = session.query( model.User ).filter_by( username=username ).first() - if user is None: - raise web.httpexceptions.HTTPNotFound() - page = trans.sa_session.query( model.Page ).filter_by( user=user, slug=slug, deleted=False, published=True ).first() + page_query_base = trans.sa_session.query( model.Page ).filter_by( user=user, slug=slug, deleted=False ) + if user is not None: + # User can view page if it's importable or if it's shared with him/her. + page = page_query_base.filter( or_( model.Page.user==trans.get_user(), model.Page.importable==True, model.Page.users_shared_with.any( model.PageUserShareAssociation.user==trans.get_user() ) ) ).first() + else: + # User not logged in, so only way to view page is if it's importable. + page = page_query_base.filter_by( importable=True ).first() if page is None: - raise web.httpexceptions.HTTPNotFound() - return trans.fill_template( "page/display.mako", item=page ) + raise web.httpexceptions.HTTPNotFound() + + return trans.fill_template_mako( "page/display.mako", item=page) + @web.expose @web.require_login("select a history from saved histories") diff -r a72dc4023e59 -r fe6e3c197d69 lib/galaxy/web/controllers/workflow.py --- a/lib/galaxy/web/controllers/workflow.py Sat Jan 23 15:52:42 2010 -0500 +++ b/lib/galaxy/web/controllers/workflow.py Sun Jan 24 17:38:05 2010 -0500 @@ -155,10 +155,10 @@ @web.expose def display_by_username_and_slug( self, trans, username, slug ): - """ View workflow based on a username and slug. """ + """ Display workflow based on a username and slug. """ session = trans.sa_session - # Get history. + # Get workflow. session = trans.sa_session user = session.query( model.User ).filter_by( username=username ).first() workflow_query_base = trans.sa_session.query( model.StoredWorkflow ).filter_by( user=user, slug=slug, deleted=False ) @@ -221,6 +221,7 @@ share.user = other session = trans.sa_session session.add( share ) + self.set_item_slug( session, stored ) session.flush() trans.set_message( "Workflow '%s' shared with user '%s'" % ( stored.name, other.email ) ) return trans.response.send_redirect( url_for( controller='workflow', action='sharing', id=id ) ) diff -r a72dc4023e59 -r fe6e3c197d69 templates/base_panels.mako --- a/templates/base_panels.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/base_panels.mako Sun Jan 24 17:38:05 2010 -0500 @@ -265,7 +265,7 @@ <li><a target="galaxy_main" href="${h.url_for( controller='/history', action='list' )}">Histories</a></li> <li><a target="galaxy_main" href="${h.url_for( controller='/dataset', action='list' )}">Datasets</a></li> %if app.config.get_bool( 'enable_pages', False ): - <li><a href="${h.url_for( controller='/page' )}">Pages</a></li> + <li><a href="${h.url_for( controller='/page', action='list' )}">Pages</a></li> %endif %endif </ul> diff -r a72dc4023e59 -r fe6e3c197d69 templates/display_base.mako --- a/templates/display_base.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/display_base.mako Sun Jan 24 17:38:05 2010 -0500 @@ -7,26 +7,16 @@ from galaxy import model %> <%inherit file="${inherit( context )}"/> -<%namespace file="./tagging_common.mako" import="render_individual_tagging_element, render_community_tagging_element" /> +<%namespace file="/tagging_common.mako" import="render_individual_tagging_element, render_community_tagging_element" /> +<%namespace file="/display_common.mako" import="*" /> <%! from galaxy.model import History, StoredWorkflow, Page from galaxy.web.framework.helpers import iff %> -## Get display name for a class. -<%def name="get_class_display_name( a_class )"> -<% - ## Start with exceptions, end with default. - if a_class is model.StoredWorkflow: - return "Workflow" - else: - return a_class.__name__ -%> -</%def> - <%def name="title()"> - Galaxy | ${iff( item.published, "Published ", iff( item.importable , "Accessible ", "Shared " ) ) + self.get_class_display_name( item.__class__ )} | ${item.name} + Galaxy | ${iff( item.published, "Published ", iff( item.importable , "Accessible ", iff( item.users_shared_with, "Shared ", "Private " ) ) ) + get_class_display_name( item.__class__ )} | ${get_item_name( item )} </%def> <%def name="init()"> @@ -88,10 +78,6 @@ Item </%def> -<%def name="get_item_name( item )"> - <% return item.name %> -</%def> - ## ## When page has no panels, center panel is body. ## @@ -120,12 +106,13 @@ <a href="${href_to_all_items}">Published ${item_plural}</a> | <a href="${href_to_user_items}">${item.user.username}</a> %elif item.importable: - Accessible ${self.get_class_display_name( item.__class__ )} + Accessible ${get_class_display_name( item.__class__ )} + %elif item.users_shared_with: + Shared ${get_class_display_name( item.__class__ )} %else: - ## Item shared with user. - Shared ${self.get_class_display_name( item.__class__ )} + Private ${get_class_display_name( item.__class__ )} %endif - | ${self.get_item_name( item )} + | ${get_item_name( item )} </div> </div> @@ -168,44 +155,4 @@ %endif </div> </div> -</%def> - - -## -## Utility methods. -## - -## Get plural term for item. -<%def name="get_item_plural( item )"> - <% - items_plural = "items" - if isinstance( item, History ): - items_plural = "Histories" - elif isinstance( item, StoredWorkflow ): - items_plural = "Workflows" - elif isinstance( item, Page ): - items_plural = "Pages" - return items_plural - %> -</%def> - -## Returns the controller name for an item based on its class. -<%def name="get_controller_name( item )"> - <% - if isinstance( item, History ): - return "history" - elif isinstance( item, StoredWorkflow ): - return "workflow" - elif isinstance( item, Page ): - return "page" - %> -</%def> - -## Return a link to view a history. -<%def name="get_history_link( history, qualify=False )"> - %if history.slug and history.user.username: - <% return h.url_for( controller='/history', action='display_by_username_and_slug', username=history.user.username, slug=history.slug, qualified=qualify ) %> - %else: - <% return h.url_for( controller='/history', action='view', id=trans.security.encode_id( history.id ), qualified=qualify ) %> - %endif </%def> \ No newline at end of file diff -r a72dc4023e59 -r fe6e3c197d69 templates/display_common.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/display_common.mako Sun Jan 24 17:38:05 2010 -0500 @@ -0,0 +1,91 @@ +## +## Utilities for sharing items and displaying shared items. +## +<%! from galaxy import model %> + +## Get display name for a class. +<%def name="get_class_display_name( a_class )"> +<% + ## Start with exceptions, end with default. + if a_class is model.StoredWorkflow: + return "Workflow" + else: + return a_class.__name__ +%> +</%def> + +<%def name="get_item_name( item )"> + <% + if type( item ) is model.Page: + return item.title + return item.name + %> +</%def> + +## Get plural display name for a class. +<%def name="get_class_plural_display_name( a_class )"> +<% + ## Start with exceptions, end with default. + if a_class is model.History: + return "Histories" + else: + return get_class_display_name( a_class ) + "s" +%> +</%def> + +## Get display name for a class. +<%def name="get_class_display_name( a_class )"> +<% + ## Start with exceptions, end with default. + if a_class is model.StoredWorkflow: + return "Workflow" + else: + return a_class.__name__ +%> +</%def> + +## Get plural term for item. +<%def name="get_item_plural( item )"> + <% return get_class_plural( item.__class__ ) %> +</%def> + +## Get plural term for class. +<%def name="get_class_plural( a_class )"> + <% + if a_class == model.History: + class_plural = "Histories" + elif a_class == model.StoredWorkflow: + class_plural = "Workflows" + elif a_class == model.Page: + class_plural = "Pages" + elif a_class == model.Library: + class_plural = "Libraries" + elif a_class == model.HistoryDatasetAssociation: + class_plural = "Datasets" + else: + class_plural = a_class.__name__ + "s" + return class_plural + %> +</%def> + +## Returns the controller name for an item based on its class. +<%def name="get_controller_name( item )"> + <% + if isinstance( item, model.History ): + return "history" + elif isinstance( item, model.StoredWorkflow ): + return "workflow" + elif isinstance( item, model.Page ): + return "page" + %> +</%def> + +## Return a link to view a history. +<%def name="get_history_link( history, qualify=False )"> + %if history.slug and history.user.username: + <% return h.url_for( controller='/history', action='display_by_username_and_slug', username=history.user.username, slug=history.slug, qualified=qualify ) %> + %else: + <% return h.url_for( controller='/history', action='view', id=trans.security.encode_id( history.id ), qualified=qualify ) %> + %endif +</%def> + diff -r a72dc4023e59 -r fe6e3c197d69 templates/grid_base.mako --- a/templates/grid_base.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/grid_base.mako Sun Jan 24 17:38:05 2010 -0500 @@ -10,19 +10,18 @@ %> <%inherit file="${inherit(context)}"/> +## +## Override methods from base.mako and base_panels.mako +## + +<%def name="center_panel()"> + ${make_grid( grid )} +</%def> + ## Render the grid's basic elements. Each of these elements can be subclassed. -<table> - <tr> - <td width="75%">${self.render_grid_header()}</td> - <td></td> - <td width="25%" id="grid-message" valign="top">${self.render_grid_message()}</td> - </tr> -</table> - -${self.render_grid_table()} - - -## Function definitions. +<%def name="body()"> + ${make_grid( grid )} +</%def> <%def name="title()">${grid.title}</%def> @@ -711,10 +710,26 @@ </style> </%def> +## +## Custom grid methods. +## + <%namespace file="./grid_common.mako" import="*" /> +<%def name="make_grid( grid )"> + <table> + <tr> + <td width="75%">${self.render_grid_header( grid )}</td> + <td></td> + <td width="25%" id="grid-message" valign="top">${self.render_grid_message( grid )}</td> + </tr> + </table> + + ${self.render_grid_table( grid )} +</%def> + ## Render grid message. -<%def name="render_grid_message()"> +<%def name="render_grid_message( grid )"> %if message: <p> <div class="${message_type}message transient-message">${util.restore_text( message )}</div> @@ -724,7 +739,7 @@ </%def> ## Render grid header. -<%def name="render_grid_header(render_title=True)"> +<%def name="render_grid_header( grid, render_title=True)"> <div class="grid-header"> %if render_title: <h2>${grid.title}</h2> @@ -740,12 +755,12 @@ </ul> %endif - ${render_grid_filters()} + ${render_grid_filters( grid )} </div> </%def> ## Render grid. -<%def name="render_grid_table(show_item_checkboxes=False)"> +<%def name="render_grid_table( grid, show_item_checkboxes=False)"> <% # Set flag to indicate whether grid has operations that operate on multiple items. multiple_item_ops_exist = False @@ -801,17 +816,17 @@ </tr> </thead> <tbody id="grid-table-body"> - ${render_grid_table_body_contents(show_item_checkboxes)} + ${render_grid_table_body_contents( grid, show_item_checkboxes )} </tbody> <tfoot> - ${render_grid_table_footer_contents(show_item_checkboxes)} + ${render_grid_table_footer_contents( grid, show_item_checkboxes )} </tfoot> </table> </form> </%def> ## Render grid table body contents. -<%def name="render_grid_table_body_contents(show_item_checkboxes=False)"> +<%def name="render_grid_table_body_contents(grid, show_item_checkboxes=False)"> <% num_rows_rendered = 0 %> %if query.count() == 0: ## No results. @@ -901,27 +916,15 @@ </%def> ## Render grid table footer contents. -<%def name="render_grid_table_footer_contents(show_item_checkboxes=False)"> +<%def name="render_grid_table_footer_contents(grid, show_item_checkboxes=False)"> ## Row for navigating among pages. - <% - # Mapping between item class and plural term for item. - items_plural = "items" - if grid.model_class == History: - items_plural = "histories" - elif grid.model_class == HistoryDatasetAssociation: - items_plural = "datasets" - elif grid.model_class == User: - items_plural = "users" - elif grid.model_class == Role: - items_plural = "roles" - elif grid.model_class == Group: - items_plural = "groups" - %> - %if show_item_checkboxes: - <td></td> - %endif + <%namespace file="/display_common.mako" import="get_class_plural" /> + <% items_plural = get_class_plural( grid.model_class ).lower() %> %if grid.use_paging and num_pages > 1: <tr id="page-links-row"> + %if show_item_checkboxes: + <td></td> + %endif <td colspan="100"> <span id='page-link-container'> ## Page links. diff -r a72dc4023e59 -r fe6e3c197d69 templates/grid_base_async.mako --- a/templates/grid_base_async.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/grid_base_async.mako Sun Jan 24 17:38:05 2010 -0500 @@ -8,8 +8,8 @@ multiple_item_ops_exist = True %> -${render_grid_table_body_contents(show_item_checkboxes=multiple_item_ops_exist)} +${render_grid_table_body_contents( grid, show_item_checkboxes=multiple_item_ops_exist )} ***** ${num_pages} ***** -${render_grid_message()} \ No newline at end of file +${render_grid_message( grid )} \ No newline at end of file diff -r a72dc4023e59 -r fe6e3c197d69 templates/grid_common.mako --- a/templates/grid_common.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/grid_common.mako Sun Jan 24 17:38:05 2010 -0500 @@ -4,7 +4,7 @@ %> ## Render a filter UI for a grid column. Filter is rendered as a table row. -<%def name="render_grid_column_filter(column)"> +<%def name="render_grid_column_filter( grid, column )"> <tr> <% column_label = column.label @@ -95,7 +95,7 @@ </%def> ## Print grid search/filtering UI. -<%def name="render_grid_filters()"> +<%def name="render_grid_filters( grid )"> ## Standard search. <div> <table><tr> @@ -103,7 +103,7 @@ <table> %for column in grid.columns: %if column.filterable == "standard": - ${render_grid_column_filter(column)} + ${render_grid_column_filter( grid, column )} %endif %endfor </table> @@ -168,7 +168,7 @@ </script> %endif - ${render_grid_column_filter(column)} + ${render_grid_column_filter( grid, column )} %endif %endfor </table> diff -r a72dc4023e59 -r fe6e3c197d69 templates/history/view.mako --- a/templates/history/view.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/history/view.mako Sun Jan 24 17:38:05 2010 -0500 @@ -1,5 +1,5 @@ <%inherit file="/base_panels.mako"/> -<%namespace file="/display_base.mako" import="get_history_link, get_controller_name" /> +<%namespace file="/display_common.mako" import="get_history_link, get_controller_name" /> <%namespace file="/root/history_common.mako" import="render_dataset" /> <%namespace file="/tagging_common.mako" import="render_individual_tagging_element, render_community_tagging_element" /> diff -r a72dc4023e59 -r fe6e3c197d69 templates/page/display.mako --- a/templates/page/display.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/page/display.mako Sun Jan 24 17:38:05 2010 -0500 @@ -1,10 +1,5 @@ <%inherit file="/display_base.mako"/> -<%def name="title()"> - <% page = item %> - Galaxy :: ${page.user.username} :: ${page.title} -</%def> - <%def name="javascripts()"> ${parent.javascripts()} ${h.js( "jquery", "json2", "jquery.jstore-all", "jquery.autocomplete", "autocomplete_tagging" )} diff -r a72dc4023e59 -r fe6e3c197d69 templates/page/editor.mako --- a/templates/page/editor.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/page/editor.mako Sun Jan 24 17:38:05 2010 -0500 @@ -212,7 +212,7 @@ "Insert Link to History", table_html + "<div><input id='make-importable' type='checkbox' checked/>" + - "Publish the selected histories so that they can viewed by everyone.</div>" + "Make the selected histories accessible so that they can viewed by everyone.</div>" , { "Insert": function() @@ -231,8 +231,8 @@ if (make_importable) $.ajax({ type: "POST", - url: '${h.url_for( controller='history', action='set_importable_async' )}', - data: { id: item_id, importable: 'True' }, + url: '${h.url_for( controller='history', action='set_accessible_async' )}', + data: { id: item_id, accessible: 'True' }, error: function() { alert('Make history importable failed; id=' + item_id) } }); @@ -412,7 +412,7 @@ }); ## Close button $("#close-button").click(function() { - <% next_url = h.url_for( controller='page', action='index' ) %> + <% next_url = h.url_for( controller='page', action='list' ) %> // var new_content = editor.xhtml(); // var changed = ( initial_content != new_content ); var changed = false; diff -r a72dc4023e59 -r fe6e3c197d69 templates/page/index.mako --- a/templates/page/index.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/page/index.mako Sun Jan 24 17:38:05 2010 -0500 @@ -16,8 +16,39 @@ <div style="overflow: auto; height: 100%;"> <div class="page-container" style="padding: 10px;"> ${grid} + + <br><br> + <h2>Pages shared with you by others</h2> + + %if shared_by_others: + <table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%"> + <tr class="header"> + <th>Title</th> + <th>Owner</th> + <th></th> + </tr> + %for i, association in enumerate( shared_by_others ): + <% page = association.page %> + <tr> + <td> + <a class="menubutton" id="shared-${i}-popup" href="${h.url_for( action='display_by_username_and_slug', username=page.user.username, slug=page.slug)}">${page.title}</a> + </td> + <td>${page.user.username}</td> + <td> + <div popupmenu="shared-${i}-popup"> + <a class="action-button" href="${h.url_for( action='display_by_username_and_slug', username=page.user.username, slug=page.slug)}" target="_top">View</a> + </div> + </td> + </tr> + %endfor + </table> + %else: + + No pages have been shared with you. + + %endif + </div> </div> - </%def> diff -r a72dc4023e59 -r fe6e3c197d69 templates/page/select_histories_grid.mako --- a/templates/page/select_histories_grid.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/page/select_histories_grid.mako Sun Jan 24 17:38:05 2010 -0500 @@ -3,8 +3,8 @@ ${javascripts()} ${stylesheets()} -${render_grid_header(False)} -${render_grid_table(show_item_checkboxes=True)} +${render_grid_header( grid, False )} +${render_grid_table( grid, show_item_checkboxes=True )} ## Initialize the grid. <script type="text/javascript"> diff -r a72dc4023e59 -r fe6e3c197d69 templates/share_base.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/share_base.mako Sun Jan 24 17:38:05 2010 -0500 @@ -0,0 +1,36 @@ +<%inherit file="/base.mako"/> + +%if message: +<% +if messagetype is UNDEFINED: + mt = "done" +else: + mt = messagetype +%> +<p /> +<div class="${mt}message"> + ${message} +</div> +<p /> +%endif + +<div class="toolForm"> + <div class="toolFormTitle">Share page '${item.title}'</div> + <div class="toolFormBody"> + <form action="${h.url_for( action='share', id=trans.security.encode_id( item.id ) )}" method="POST"> + <div class="form-row"> + <label> + Email address of user to share with + </label> + <div style="float: left; width: 250px; margin-right: 10px;"> + <input type="text" name="email" value="${email}" size="40"> + </div> + <div style="clear: both"></div> + </div> + <div class="form-row"> + <input type="submit" value="Share"></input> + </div> + </form> + </div> + </div> +</div> \ No newline at end of file diff -r a72dc4023e59 -r fe6e3c197d69 templates/sharing_base.mako --- a/templates/sharing_base.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/sharing_base.mako Sun Jan 24 17:38:05 2010 -0500 @@ -4,14 +4,15 @@ ## <%inherit file="/base.mako"/> -<%! from galaxy import model %> + +<%namespace file="./display_common.mako" import="*" /> ## ## Page methods. ## <%def name="title()"> - Sharing and Publishing ${get_class_display_name( item.__class__ )} '${item.name}' + Sharing and Publishing ${get_class_display_name( item.__class__ )} '${get_item_name( item )}' </%def> <%def name="stylesheets()"> @@ -28,177 +29,155 @@ </style> </%def> -## Get display name for a class. -<%def name="get_class_display_name( a_class )"> -<% - ## Start with exceptions, end with default. - if a_class is model.StoredWorkflow: - return "Workflow" - else: - return a_class.__name__ -%> -</%def> +<%def name="body()"> + <% + # + # Setup and variables needed for page. + # + + # Get class name strings. + item_class_name = get_class_display_name( item.__class__ ) + item_class_name_lc = item_class_name.lower() + item_class_plural_name = get_class_plural_display_name( item.__class__ ) + item_class_plural_name_lc = item_class_plural_name.lower() + %> + <% item_name = get_item_name(item) %> -## Get plural display name for a class. -<%def name="get_class_plural_display_name( a_class )"> -<% - ## Start with exceptions, end with default. - if a_class is model.History: - return "Histories" - else: - return get_class_display_name( a_class ) + "s" -%> -</%def> + <h2>Sharing and Publishing ${item_class_name} '${item_name}'</h2> -## -## Page content. -## -<% - # - # Setup and variables needed for page. - # + ## Require that user have a public username before sharing or publishing an item. + %if trans.get_user().username is None or trans.get_user().username is "": + To make a ${item_class_name_lc} accessible via link or publish it, you must create a public username: + <p> + <form action="${h.url_for( action='set_public_username', id=trans.security.encode_id( item.id ) )}" + method="POST"> + <div class="form-row"> + <label>Public Username:</label> + <div class="form-row-input"> + <input type="text" name="username" size="40"/> + </div> + </div> + <div style="clear: both"></div> + <div class="form-row"> + <input class="action-button" type="submit" name="Set Username" value="Set Username"/> + </div> + </form> + %else: + ## User has a public username, so private sharing and publishing options. + <div class="indent" style="margin-top: 2em"> + <h3>Making ${item_class_name} Accessible via Link and Publishing It</h3> - # Get class name strings. - item_class_name = get_class_display_name( item.__class__ ) - item_class_name_lc = item_class_name.lower() - item_class_plural_name = get_class_plural_display_name( item.__class__ ) - item_class_plural_name_lc = item_class_plural_name.lower() -%> + <div class="indent"> + %if item.importable: + <% + item_status = "accessible via link" + if item.published: + item_status = item_status + " and published" + %> + This ${item_class_name_lc} <strong>${item_status}</strong>. + <div class="indent"> + <p>Anyone can view and import this ${item_class_name_lc} by visiting the following URL: + <% url = h.url_for( action='display_by_username_and_slug', username=trans.get_user().username, slug=item.slug, qualified=True ) %> + <blockquote> + <a href="${url}" target="_top">${url}</a> + </blockquote> + + %if item.published: + This ${item_class_name_lc} is publicly listed and searchable in Galaxy's <a href='${h.url_for( action='list_published' )}' target="_top">Published ${item_class_plural_name}</a> section. + %endif + </div> + + <p>You can: + <div class="indent"> + <form action="${h.url_for( action='sharing', id=trans.security.encode_id( item.id ) )}" + method="POST"> + %if not item.published: + ## Item is importable but not published. User can disable importable or publish. + <input class="action-button" type="submit" name="disable_link_access" value="Disable Access to ${item_class_name} Link"> + <div class="toolParamHelp">Disables ${item_class_name_lc}'s link so that it is not accessible.</div> + <br> + <input class="action-button" type="submit" name="publish" value="Publish ${item_class_name}" method="POST"> + <div class="toolParamHelp">Publishes the ${item_class_name_lc} to Galaxy's <a href='${h.url_for( action='list_published' )}' target="_top">Published ${item_class_plural_name}</a> section, where it is publicly listed and searchable.</div> -<h2>Sharing and Publishing ${item_class_name} '${item.name}'</h2> + <br> + %else: ## item.published == True + ## Item is importable and published. User can unpublish or disable import and unpublish. + <input class="action-button" type="submit" name="unpublish" value="Unpublish ${item_class_name}"> + <div class="toolParamHelp">Removes ${item_class_name_lc} from Galaxy's <a href='${h.url_for( action='list_published' )}' target="_top">Published ${item_class_plural_name}</a> section so that it is not publicly listed or searchable.</div> + <br> + <input class="action-button" type="submit" name="disable_link_access_and_unpubish" value="Disable Access to ${item_class_name} via Link and Unpublish"> + <div class="toolParamHelp">Disables ${item_class_name_lc}'s link so that it is not accessible and removes ${item_class_name_lc} from Galaxy's <a href='${h.url_for( action='list_published' )}' target='_top'>Published ${item_class_plural_name}</a> section so that it is not publicly listed or searchable.</div> + %endif + + </form> + </div> + + %else: + + This ${item_class_name_lc} is currently restricted so that only you and the users listed below can access it. You can: + <p> + <form action="${h.url_for( action='sharing', id=trans.security.encode_id(item.id) )}" method="POST"> + <input class="action-button" type="submit" name="make_accessible_via_link" value="Make ${item_class_name} Accessible via Link"> + <div class="toolParamHelp">Generates a web link that you can share with other people so that they can view and import the ${item_class_name_lc}.</div> + + <br> + <input class="action-button" type="submit" name="make_accessible_and_publish" value="Make ${item_class_name} Accessible and Publish" method="POST"> + <div class="toolParamHelp">Makes the ${item_class_name_lc} accessible via link (see above) and publishes the ${item_class_name_lc} to Galaxy's <a href='${h.url_for( action='list_published' )}' target='_top'>Published ${item_class_plural_name}</a> section, where it is publicly listed and searchable.</div> + </form> + + %endif + </div> -## Require that user have a public username before sharing or publishing an item. -%if trans.get_user().username is None or trans.get_user().username is "": - To make a ${item_class_name_lc} accessible via link or publish it, you must create a public username: - <p> - <form action="${h.url_for( action='set_public_username', id=trans.security.encode_id( item.id ) )}" - method="POST"> - <div class="form-row"> - <label>Public Username:</label> - <div class="form-row-input"> - <input type="text" name="username" size="40"/> - </div> + <h3>Sharing ${item_class_name} with Specific Users</h3> + + <div class="indent"> + %if item.users_shared_with: + + <p> + The following users will see this ${item_class_name_lc} in their ${item_class_name_lc} list and will be + able to run/view and import it. + </p> + + <table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%"> + <tr class="header"> + <th>Email</th> + <th></th> + </tr> + %for i, association in enumerate( item.users_shared_with ): + <% user = association.user %> + <tr> + <td> + ${user.email} + <a id="user-${i}-popup" class="popup-arrow" style="display: none;">▼</a> + </td> + <td> + <div popupmenu="user-${i}-popup"> + <a class="action-button" href="${h.url_for( action='sharing', id=trans.security.encode_id( item.id ), unshare_user=trans.security.encode_id( user.id ) )}">Unshare</a> + </div> + </td> + </tr> + %endfor + </table> + + <p> + <a class="action-button" href="${h.url_for( action='share', id=trans.security.encode_id(item.id) )}"> + <span>Share with another user</span> + </a> + + %else: + + <p>You have not shared this ${item_class_name_lc} with any users.</p> + + <a class="action-button" href="${h.url_for( action='share', id=trans.security.encode_id(item.id) )}"> + <span>Share with a user</span> + </a> + <br> + + %endif + </div> </div> - <div style="clear: both"></div> - <div class="form-row"> - <input class="action-button" type="submit" name="Set Username" value="Set Username"/> - </div> - </form> -%else: - ## User has a public username, so private sharing and publishing options. - <div class="indent" style="margin-top: 2em"> - <h3>Making ${item_class_name} Accessible via Link and Publishing It</h3> - - <div class="indent"> - %if item.importable: - <% - item_status = "accessible via link" - if item.published: - item_status = item_status + " and published" - %> - This ${item_class_name_lc} <strong>${item_status}</strong>. - <div class="indent"> - <p>Anyone can view and import this ${item_class_name_lc} by visiting the following URL: - <% url = h.url_for( action='display_by_username_and_slug', username=trans.get_user().username, slug=item.slug, qualified=True ) %> - <blockquote> - <a href="${url}" target="_top">${url}</a> - </blockquote> - - %if item.published: - This ${item_class_name_lc} is publicly listed and searchable in Galaxy's <a href='${h.url_for( action='list_published' )}' target="_top">Published ${item_class_plural_name}</a> section. - %endif - </div> - - <p>You can: - <div class="indent"> - <form action="${h.url_for( action='sharing', id=trans.security.encode_id( item.id ) )}" - method="POST"> - %if not item.published: - ## Item is importable but not published. User can disable importable or publish. - <input class="action-button" type="submit" name="disable_link_access" value="Disable Access to ${item_class_name} Link"> - <div class="toolParamHelp">Disables ${item_class_name_lc}'s link so that it is not accessible.</div> - <br> - <input class="action-button" type="submit" name="publish" value="Publish ${item_class_name}" method="POST"> - <div class="toolParamHelp">Publishes the ${item_class_name_lc} to Galaxy's <a href='${h.url_for( action='list_published' )}' target="_top">Published ${item_class_plural_name}</a> section, where it is publicly listed and searchable.</div> + %endif - <br> - %else: ## item.published == True - ## Item is importable and published. User can unpublish or disable import and unpublish. - <input class="action-button" type="submit" name="unpublish" value="Unpublish ${item_class_name}"> - <div class="toolParamHelp">Removes ${item_class_name_lc} from Galaxy's <a href='${h.url_for( action='list_published' )}' target="_top">Published ${item_class_plural_name}</a> section so that it is not publicly listed or searchable.</div> - <br> - <input class="action-button" type="submit" name="disable_link_access_and_unpubish" value="Disable Access to ${item_class_name} via Link and Unpublish"> - <div class="toolParamHelp">Disables ${item_class_name_lc}'s link so that it is not accessible and removes ${item_class_name_lc} from Galaxy's <a href='${h.url_for( action='list_published' )}' target='_top'>Published ${item_class_plural_name}</a> section so that it is not publicly listed or searchable.</div> - %endif - - </form> - </div> - - %else: - - This ${item_class_name_lc} is currently restricted so that only you and the users listed below can access it. You can: - <p> - <form action="${h.url_for( action='sharing', id=trans.security.encode_id(item.id) )}" method="POST"> - <input class="action-button" type="submit" name="make_accessible_via_link" value="Make ${item_class_name} Accessible via Link"> - <div class="toolParamHelp">Generates a web link that you can share with other people so that they can view and import the ${item_class_name_lc}.</div> - - <br> - <input class="action-button" type="submit" name="make_accessible_and_publish" value="Make ${item_class_name} Accessible and Publish" method="POST"> - <div class="toolParamHelp">Makes the ${item_class_name_lc} accessible via link (see above) and publishes the ${item_class_name_lc} to Galaxy's <a href='${h.url_for( action='list_published' )}' target='_top'>Published ${item_class_plural_name}</a> section, where it is publicly listed and searchable.</div> - </form> - - %endif - </div> - - <h3>Sharing ${item_class_name} with Specific Users</h3> - - <div class="indent"> - %if item.users_shared_with: - - <p> - The following users will see this ${item_class_name_lc} in their ${item_class_name_lc} list and will be - able to run/view and import it. - </p> - - <table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%"> - <tr class="header"> - <th>Email</th> - <th></th> - </tr> - %for i, association in enumerate( item.users_shared_with ): - <% user = association.user %> - <tr> - <td> - ${user.email} - <a id="user-${i}-popup" class="popup-arrow" style="display: none;">▼</a> - </td> - <td> - <div popupmenu="user-${i}-popup"> - <a class="action-button" href="${h.url_for( action='sharing', id=trans.security.encode_id( item.id ), unshare_user=trans.security.encode_id( user.id ) )}">Unshare</a> - </div> - </td> - </tr> - %endfor - </table> - - <p> - <a class="action-button" href="${h.url_for( action='share', id=trans.security.encode_id(item.id) )}"> - <span>Share with another user</span> - </a> - - %else: - - <p>You have not shared this ${item_class_name_lc} with any users.</p> - - <a class="action-button" href="${h.url_for( action='share', id=trans.security.encode_id(item.id) )}"> - <span>Share with a user</span> - </a> - <br> - - %endif - </div> - </div> -%endif - -<p><br><br> -<a href=${h.url_for( action="list" )}>Back to ${item_class_plural_name} List</a> \ No newline at end of file + <p><br><br> + <a href=${h.url_for( action="list" )}>Back to ${item_class_plural_name} List</a> +</%def> \ No newline at end of file diff -r a72dc4023e59 -r fe6e3c197d69 templates/workflow/display.mako --- a/templates/workflow/display.mako Sat Jan 23 15:52:42 2010 -0500 +++ b/templates/workflow/display.mako Sun Jan 24 17:38:05 2010 -0500 @@ -73,7 +73,7 @@ %if workflow.user != trans.get_user(): <a href="${h.url_for( controller='/workflow', action='imp', id=trans.security.encode_id(workflow.id) )}">import and start using workflow</a> %else: - you own this workflow + your workflow %endif </%def>