details: http://www.bx.psu.edu/hg/galaxy/rev/c7607fba91b9 changeset: 3741:c7607fba91b9 user: Greg Von Kuster <greg@bx.psu.edu> date: Tue May 04 14:21:21 2010 -0400 description: Fix the recentrly introduced grid filter / search bug. Revert the recently introduced grid filter_params hack for generating links since we now use a different approach to rendering links. Convert a few more 'message_type' params to be 'status' instead. Many fixes for the community space app. diffstat: lib/galaxy/tools/__init__.py | 4 +- lib/galaxy/web/framework/__init__.py | 2 +- lib/galaxy/web/framework/helpers/grids.py | 17 +- lib/galaxy/webapps/community/controllers/admin.py | 154 +++++++++++++------- lib/galaxy/webapps/community/controllers/common.py | 90 ++++++++--- lib/galaxy/webapps/community/controllers/tool.py | 157 +++++++++++--------- lib/galaxy/webapps/community/controllers/upload.py | 33 ++-- lib/galaxy/webapps/community/model/__init__.py | 4 +- templates/dataset/display_application/display.mako | 4 +- templates/display_common.mako | 4 +- templates/grid_base.mako | 16 +- templates/grid_base_async.mako | 2 +- templates/message.mako | 12 +- templates/page/select_items_grid_async.mako | 2 +- templates/webapps/community/admin/center.mako | 39 ++++- templates/webapps/community/tool/edit_tool.mako | 19 +- templates/webapps/community/tool/view_tool.mako | 25 ++- 17 files changed, 354 insertions(+), 230 deletions(-) diffs (1213 lines): diff -r 68fc85a43bb8 -r c7607fba91b9 lib/galaxy/tools/__init__.py --- a/lib/galaxy/tools/__init__.py Tue May 04 14:19:44 2010 -0400 +++ b/lib/galaxy/tools/__init__.py Tue May 04 14:21:21 2010 -0400 @@ -837,7 +837,7 @@ assert isinstance( out_data, odict ) return 'tool_executed.mako', dict( out_data=out_data ) except: - return 'message.mako', dict( message_type='error', message='odict not returned from tool execution', refresh_frames=[] ) + return 'message.mako', dict( status='error', message='odict not returned from tool execution', refresh_frames=[] ) # Otherwise move on to the next page else: state.page += 1 @@ -890,7 +890,7 @@ self.sa_session.add( data ) self.sa_session.flush() # It's unlikely the user will ever see this. - return 'message.mako', dict( message_type='error', message='Your upload was interrupted. If this was uninentional, please retry it.', refresh_frames=[], cont=None ) + return 'message.mako', dict( status='error', message='Your upload was interrupted. If this was uninentional, please retry it.', refresh_frames=[], cont=None ) def update_state( self, trans, inputs, state, incoming, prefix="", context=None, update_only=False, old_errors={}, item_callback=None ): diff -r 68fc85a43bb8 -r c7607fba91b9 lib/galaxy/web/framework/__init__.py --- a/lib/galaxy/web/framework/__init__.py Tue May 04 14:19:44 2010 -0400 +++ b/lib/galaxy/web/framework/__init__.py Tue May 04 14:21:21 2010 -0400 @@ -591,7 +591,7 @@ `refresh_frames`: names of frames in the interface that should be refreshed when the message is displayed """ - return self.fill_template( "message.mako", message_type=type, message=message, refresh_frames=refresh_frames, cont=cont, use_panels=use_panels, active_view=active_view ) + return self.fill_template( "message.mako", status=type, message=message, refresh_frames=refresh_frames, cont=cont, use_panels=use_panels, active_view=active_view ) def show_error_message( self, message, refresh_frames=[], use_panels=False, active_view="" ): """ Convenience method for displaying an error message. See `show_message`. diff -r 68fc85a43bb8 -r c7607fba91b9 lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py Tue May 04 14:19:44 2010 -0400 +++ b/lib/galaxy/web/framework/helpers/grids.py Tue May 04 14:21:21 2010 -0400 @@ -134,13 +134,16 @@ column_filter = unicode(column_filter) extra_url_args[ "f-" + column.key ] = column_filter.encode("utf-8") # Process sort arguments. - sort_key = sort_order = None + sort_key = None + sort_order = None if 'sort' in kwargs: sort_key = kwargs['sort'] elif base_sort_key: sort_key = base_sort_key encoded_sort_key = sort_key if sort_key: + # TODO: what if the model object of the grid column being sorted is not + # the same object as self.model_class? if sort_key.startswith( "-" ): sort_key = sort_key[1:] sort_order = 'desc' @@ -230,10 +233,10 @@ current_item=current_item, ids = kwargs.get( 'id', [] ), url = url, - message_type = status, + status = status, message = message, use_panels=use_panels, - webapp=self.webapp, + webapp=webapp, # Pass back kwargs so that grid template can set and use args without # grid explicitly having to pass them. kwargs=kwargs ) @@ -290,9 +293,7 @@ if self.format: value = self.format( value ) return value - def get_link( self, trans, grid, item, filter_params ): - # FIXME: filter_params is only here so we can do grid filtering from - # column links. remove once a better way is created. + def get_link( self, trans, grid, item ): if self.link and self.link( item ): return self.link( item ) return None @@ -449,7 +450,7 @@ class PublicURLColumn( TextColumn ): """ Column displays item's public URL based on username and slug. """ - def get_link( self, trans, grid, item, filter_params ): + def get_link( self, trans, grid, item ): if item.user.username and item.slug: return dict( action='display_by_username_and_slug', username=item.user.username, slug=item.slug ) elif not item.user.username: @@ -485,7 +486,7 @@ if item.published: sharing_statuses.append( "Published" ) return ", ".join( sharing_statuses ) - def get_link( self, trans, grid, item, filter_params ): + def get_link( self, trans, grid, item ): if not item.deleted and ( item.users_shared_with or item.importable or item.published ): return dict( operation="share or publish", id=item.id ) return None diff -r 68fc85a43bb8 -r c7607fba91b9 lib/galaxy/webapps/community/controllers/admin.py --- a/lib/galaxy/webapps/community/controllers/admin.py Tue May 04 14:19:44 2010 -0400 +++ b/lib/galaxy/webapps/community/controllers/admin.py Tue May 04 14:21:21 2010 -0400 @@ -47,7 +47,7 @@ return "" class ToolsColumn( grids.TextColumn ): def get_value( self, trans, grid, user ): - return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( user.id ), str( len( user.tools ) ) ) + return '<a href="browse_tools_by_user?operation=browse&id=%s&webapp=community">%s</a>' % ( trans.security.encode_id( user.id ), str( len( user.tools ) ) ) # Grid definition webapp = "community" @@ -77,7 +77,10 @@ attach_popup=False, filterable="advanced" ), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn( "Deleted", + key="deleted", + visible=False, + filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", cols_to_filter=[ columns[0], columns[1] ], @@ -168,7 +171,10 @@ UsersColumn( "Users", attach_popup=False ), StatusColumn( "Status", attach_popup=False ), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn( "Deleted", + key="deleted", + visible=False, + filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", cols_to_filter=[ columns[0], columns[1], columns[2] ], @@ -238,16 +244,20 @@ default_sort_key = "name" columns = [ NameColumn( "Name", - key="name", + #key="name", link=( lambda item: dict( operation="Manage users and roles", id=item.id, webapp="community" ) ), model_class=model.Group, - attach_popup=True, - filterable="advanced" ), + attach_popup=True + #filterable="advanced" + ), UsersColumn( "Users", attach_popup=False ), RolesColumn( "Roles", attach_popup=False ), StatusColumn( "Status", attach_popup=False ), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn( "Deleted", + key="deleted", + visible=False, + filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", cols_to_filter=[ columns[0], columns[1], columns[2] ], @@ -309,14 +319,18 @@ attach_popup=False, filterable="advanced" ), DescriptionColumn( "Description", + key="description", model_class=model.Category, attach_popup=False, filterable="advanced" ), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn( "Deleted", + key="deleted", + visible=False, + filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1], columns[2] ], + cols_to_filter=[ columns[0], columns[1] ], key="free-text-search", visible=False, filterable="standard" ) ) @@ -370,24 +384,34 @@ default_sort_key = "name" columns = [ NameColumn( "Name", - key="name", + # TODO: we cannot currently sort by columns since the grid may be filtered by tool ids + # and it is not clear if / how that will work. We need to be able to send to the grid helper + # the list of ids on which to filter when sorting on the column. + #key="name", + model_class=model.Category, link=( lambda item: dict( operation="Browse Category", id=item.id, webapp="community" ) ), - model_class=model.Category, - attach_popup=True, - filterable="advanced" ), + attach_popup=False + #filterable="advanced" + ), DescriptionColumn( "Description", + #key="description", model_class=model.Category, - attach_popup=False, - filterable="advanced" ), + attach_popup=False + #filterable="advanced" + ), ToolsColumn( "Tools", - model_class=model.Category, + model_class=model.Tool, attach_popup=False, filterable="advanced" ), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn( "Deleted", + key="deleted", + visible=False, + filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1], columns[2] ], + #cols_to_filter=[ columns[0], columns[1] ], + cols_to_filter=[], key="free-text-search", visible=False, filterable="standard" ) ) @@ -399,10 +423,17 @@ num_rows_per_page = 50 preserve_state = False use_paging = True - def get_current_item( self, trans ): - return None def build_initial_query( self, session ): return session.query( self.model_class ) + def apply_default_filter( self, trans, query, **kwd ): + ids = kwd.get( 'ids', False ) + if ids: + if str( ids ).lower() == 'none': + # No tools for display + return query.filter( model.Tool.id == None ) + ids = util.listify( ids ) + query = query.filter( or_( *map( lambda id: self.model_class.id == id, ids ) ) ) + return query class ToolListGrid( grids.Grid ): class NameColumn( grids.TextColumn ): @@ -419,7 +450,7 @@ if tool.categories: rval = '' for tca in tool.categories: - rval += '<a href="browse_category?id=%s">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name ) + rval += '<a href="browse_category?id=%s&webapp=community">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name ) return rval return 'not set' class StateColumn( grids.GridColumn ): @@ -450,7 +481,7 @@ return accepted_filters class UserColumn( grids.TextColumn ): def get_value( self, trans, grid, tool ): - return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username ) + return '<a href="browse_tools_by_user?operation=browse&id=%s&webapp=community">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username ) # Grid definition title = "Tools" model_class = model.Tool @@ -458,19 +489,25 @@ default_sort_key = "name" columns = [ NameColumn( "Name", - key="name", + # TODO: we cannot currently sort by columns since the grid may be filtered by tool ids + # and it is not clear if / how that will work. We need to be able to send to the grid helper + # the list of ids on which to filter when sorting on the column. + #key="name", + link=( lambda item: dict( operation="View Tool", id=item.id, cntrller="admin", webapp="community" ) ), model_class=model.Tool, - link=( lambda item: dict( operation="View Tool", id=item.id, cntrller='admin', webapp="community" ) ), - attach_popup=True, - filterable="advanced" ), + attach_popup=True + #filterable="advanced" + ), VersionColumn( "Version", model_class=model.Tool, attach_popup=False, filterable="advanced" ), DescriptionColumn( "Description", - model_class=model.Tool, - attach_popup=False, - filterable="advanced" ), + #key="description", + model_class=model.Tool, + attach_popup=False + #filterable="advanced" + ), CategoryColumn( "Category", model_class=model.Category, attach_popup=False, @@ -479,15 +516,19 @@ model_class=model.Event, attach_popup=False ), UserColumn( "Uploaded By", - key="username", model_class=model.User, attach_popup=False, filterable="advanced" ), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", model_class=model.Tool, key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn( "Deleted", + model_class=model.Tool, + key="deleted", + visible=False, + filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1] ], + #cols_to_filter=[ columns[0], columns[2], columns[5] ], + cols_to_filter=[], key="free-text-search", visible=False, filterable="standard" ) ) @@ -499,7 +540,7 @@ ] standard_filters = [ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), - grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + grids.GridColumnFilter( "All", args=dict( deleted='All' ) ), ] default_filter = dict( name="All", deleted="False" ) num_rows_per_page = 50 @@ -508,15 +549,14 @@ def build_initial_query( self, session ): return session.query( self.model_class ) def apply_default_filter( self, trans, query, **kwd ): - tool_id = kwd.get( 'tool_id', False ) - if tool_id: - if str( tool_id ).lower() in [ '', 'none' ]: - # Return an empty query since the current user cannot view any - # tools (possibly due to state not being approved, etc). + ids = kwd.get( 'ids', False ) + if ids: + if str( ids ).lower() == 'none': + # No tools for display return query.filter( model.Tool.id == None ) - tool_id = util.listify( tool_id ) - query = query.filter( or_( *map( lambda id: self.model_class.id == id, tool_id ) ) ) - return query.filter( self.model_class.deleted==False ) + ids = util.listify( ids ) + query = query.filter( or_( *map( lambda id: self.model_class.id == id, ids ) ) ) + return query class AdminController( BaseController, Admin ): @@ -616,12 +656,12 @@ status='error' ) ) event = get_event( trans, id ) state = event.state - tool_id = get_tools_by_state( trans, state ) - if not tool_id: - tool_id = 'None' + ids = get_tools_by_state( trans, state ) + if not ids: + ids = 'none' return trans.response.send_redirect( web.url_for( controller='admin', action='browse_tools', - tool_id=tool_id ) ) + ids=ids ) ) @web.expose @web.require_admin def browse_category( self, trans, **kwd ): @@ -681,7 +721,10 @@ # If we're approving a tool, all previous versions must be set to archived for version in get_versions( trans, tool ): if version != tool and version.is_approved(): - self.set_tool_state( trans, trans.app.model.Tool.states.ARCHIVED, id=trans.app.security.encode_id( version.id ), redirect='False' ) + self.set_tool_state( trans, + trans.app.model.Tool.states.ARCHIVED, + id=trans.security.encode_id( version.id ), + redirect='False' ) event = trans.model.Event( state ) # Flush so we an get an id trans.sa_session.add( event ) @@ -842,33 +885,34 @@ ## ---- Utility methods ------------------------------------------------------- def get_tools_by_state( trans, state ): - tool_id = [] + # TODO: write this as a query using eagerload - will be much faster. + ids = [] if state == trans.model.Tool.states.NEW: for tool in get_tools( trans ): if tool.is_new(): - tool_id.append( tool.id ) + ids.append( tool.id ) elif state == trans.model.Tool.states.ERROR: for tool in get_tools( trans ): if tool.is_error(): - tool_id.append( tool.id ) + ids.append( tool.id ) elif state == trans.model.Tool.states.DELETED: for tool in get_tools( trans ): if tool.is_deleted(): - tool_id.append( tool.id ) + ids.append( tool.id ) elif state == trans.model.Tool.states.WAITING: for tool in get_tools( trans ): if tool.is_waiting(): - tool_id.append( tool.id ) + ids.append( tool.id ) elif state == trans.model.Tool.states.APPROVED: for tool in get_tools( trans ): if tool.is_approved(): - tool_id.append( tool.id ) + ids.append( tool.id ) elif state == trans.model.Tool.states.REJECTED: for tool in get_tools( trans ): if tool.is_rejected(): - tool_id.append( tool.id ) + ids.append( tool.id ) elif state == trans.model.Tool.states.ARCHIVED: for tool in get_tools( trans ): if tool.is_archived(): - tool_id.append( tool.id ) - return tool_id + ids.append( tool.id ) + return ids diff -r 68fc85a43bb8 -r c7607fba91b9 lib/galaxy/webapps/community/controllers/common.py --- a/lib/galaxy/webapps/community/controllers/common.py Tue May 04 14:19:44 2010 -0400 +++ b/lib/galaxy/webapps/community/controllers/common.py Tue May 04 14:21:21 2010 -0400 @@ -91,6 +91,35 @@ message=message, status=status ) @web.expose + def delete_tool( self, trans, cntrller, **kwd ): + params = util.Params( kwd ) + message = util.restore_text( params.get( 'message', '' ) ) + status = params.get( 'status', 'done' ) + id = params.get( 'id', None ) + if not id: + message='Select a tool to delete' + status='error' + else: + tool = get_tool( trans, id ) + # Create a new event + event = trans.model.Event( state=trans.model.Tool.states.DELETED ) + # Flush so we can get an event id + trans.sa_session.add( event ) + trans.sa_session.flush() + # Associate the tool with the event + tea = trans.model.ToolEventAssociation( tool=tool, event=event ) + # Delete the tool, keeping state for categories, events and versions + tool.deleted = True + trans.sa_session.add_all( ( tool, tea ) ) + trans.sa_session.flush() + # TODO: What if the tool has versions, should they all be deleted? + message = "Tool '%s' has been marked deleted" + status = 'done' + return trans.response.send_redirect( web.url_for( controller=cntrller, + action='browse_tools', + message=message, + status=status ) ) + @web.expose def upload_new_tool_version( self, trans, cntrller, **kwd ): params = util.Params( kwd ) message = util.restore_text( params.get( 'message', '' ) ) @@ -122,15 +151,15 @@ # If request came from the tool controller, then we need to filter by the state of the # tool in addition to the category. if cntrller == 'tool': - tool_id = get_approved_tools( trans, category=category ) + ids = get_approved_tools( trans, category=category ) else: # If request came from the admin controller, we don't filter on tool state. - tool_id = [ tca.tool.id for tca in category.tools ] - if not tool_id: - tool_id = 'None' + ids = [ tca.tool.id for tca in category.tools ] + if not ids: + ids = 'none' return trans.response.send_redirect( web.url_for( controller=cntrller, action='browse_tools', - tool_id=tool_id ) ) + ids=ids ) ) @web.expose def browse_tools_by_user( self, trans, cntrller, **kwd ): params = util.Params( kwd ) @@ -146,15 +175,21 @@ # If request came from the tool controller, then we need to filter by the state of the # tool if the user is not viewing his own tools if cntrller == 'tool': - tool_id = get_approved_tools( trans, user=user ) + ids = get_tools_uploaded_by( trans, user ) else: - # If request came from the admin controller, we don't filter on tool state. - tool_id = [ tool.id for tool in user.tools ] - if not tool_id: - tool_id = 'None' + # If request came from the admin controller we don't filter on tool state. + ids = [ tool.id for tool in user.tools ] + if not ids: + ids = 'none' + if cntrller == 'tool' and user != trans.user: + # If the user is browsing someone else's tools, then we do not want to + # use the BrowseToolsByUser list grid since it includes a status column. + return trans.response.send_redirect( web.url_for( controller=cntrller, + action='browse_tools', + ids=ids ) ) return trans.response.send_redirect( web.url_for( controller=cntrller, action='browse_tools_by_user', - tool_id=tool_id ) ) + ids=ids ) ) ## ---- Utility methods ------------------------------------------------------- @@ -202,30 +237,33 @@ return trans.sa_session.query( trans.model.Tool ).get( trans.app.security.decode_id( id ) ) def get_tools( trans ): return trans.sa_session.query( trans.model.Tool ).order_by( trans.model.Tool.name ) -def get_approved_tools( trans, category=None, user=None ): - tool_id = [] +def get_approved_tools( trans, category=None ): + # TODO: write this as a query using eagerload - will be much faster. + ids = [] if category: # Return only the approved tools in the category for tca in category.tools: tool = tca.tool if tool.is_approved(): - tool_id.append( tool.id ) - elif user: - if trans.user == user: - # If the current user is browsing his own tools, then don't filter on state - tool_id = [ tool.id for tool in user.tools ] - else: - # The current user is viewing all tools uploaded by another user, so show only - # approved tools - for tool in user.active_tools: - if tool.is_approved(): - tool_id.append( tool.id ) + ids.append( tool.id ) else: # Return all approved tools for tool in get_tools( trans ): if tool.is_approved(): - tool_id.append( tool.id ) - return tool_id + ids.append( tool.id ) + return ids +def get_tools_uploaded_by( trans, user ): + # TODO: write this as a query using eagerload - will be much faster. + ids = [] + if trans.user == user: + # If the current user is browsing his own tools, then don't filter on state + ids = [ tool.id for tool in user.tools ] + else: + # The current user is viewing tools uploaded by another user, so show only approved tools + for tool in user.active_tools: + if tool.is_approved(): + ids.append( tool.id ) + return ids def get_event( trans, id ): return trans.sa_session.query( trans.model.Event ).get( trans.security.decode_id( id ) ) def get_user( trans, id ): diff -r 68fc85a43bb8 -r c7607fba91b9 lib/galaxy/webapps/community/controllers/tool.py --- a/lib/galaxy/webapps/community/controllers/tool.py Tue May 04 14:19:44 2010 -0400 +++ b/lib/galaxy/webapps/community/controllers/tool.py Tue May 04 14:21:21 2010 -0400 @@ -26,12 +26,12 @@ if tool.categories: rval = '' for tca in tool.categories: - rval += '<a href="browse_category?id=%s">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name ) + rval += '<a href="browse_category?id=%s&webapp=community">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name ) return rval return 'not set' class UserColumn( grids.TextColumn ): def get_value( self, trans, grid, tool ): - return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username ) + return '<a href="browse_tools_by_user?operation=browse&id=%s&webapp=community">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username ) # Grid definition title = "Tools" model_class = model.Tool @@ -39,33 +39,44 @@ default_sort_key = "name" columns = [ NameColumn( "Name", - key="name", + # TODO: we cannot currently sort by columns since the grid may be filtered by tool ids + # and it is not clear if / how that will work. We need to be able to send to the grid helper + # the list of ids on which to filter when sorting on the column. + #key="name", model_class=model.Tool, link=( lambda item: dict( operation="View Tool", id=item.id, cntrller='tool', webapp="community" ) ), - attach_popup=True, - filterable="advanced" ), + attach_popup=True + #filterable="advanced" + ), VersionColumn( "Version", model_class=model.Tool, attach_popup=False, filterable="advanced" ), DescriptionColumn( "Description", - model_class=model.Tool, - attach_popup=False, - filterable="advanced" ), + #key="description", + model_class=model.Tool, + attach_popup=False + #filterable="advanced" + ), CategoryColumn( "Categories", model_class=model.Category, attach_popup=False, filterable="advanced" ), UserColumn( "Uploaded By", - key="username", + #key="username", model_class=model.User, - attach_popup=False, - filterable="advanced" ), + attach_popup=False + #filterable="advanced" + ), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn( "Deleted", + key="deleted", + visible=False, + filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1] ], + #cols_to_filter=[ columns[0], columns[2], columns[4] ], + cols_to_filter=[], key="free-text-search", visible=False, filterable="standard" ) ) @@ -86,21 +97,15 @@ def build_initial_query( self, session ): return session.query( self.model_class ) def apply_default_filter( self, trans, query, **kwd ): - def filter_query( query, tool_id ): - if str( tool_id ).lower() in [ '', 'none' ]: - # Return an empty query since the current user cannot view any - # tools (possibly due to state not being approved, etc). - return query.filter( model.Tool.id == None ) - tool_id = util.listify( tool_id ) - query = query.filter( or_( *map( lambda id: self.model_class.id == id, tool_id ) ) ) - return query.filter( self.model_class.deleted==False ) - tool_id = kwd.get( 'tool_id', False ) - if not tool_id: + ids = kwd.get( 'ids', False ) + if not ids: # Display only approved tools - tool_id = get_approved_tools( trans ) - if not tool_id: - tool_id = 'None' - return filter_query( query, tool_id ) + ids = get_approved_tools( trans ) + if not ids or str( ids ).lower() == 'none': + return query.filter( trans.model.Tool.id == None ) + ids = util.listify( ids ) + query = query.filter( or_( *map( lambda id: self.model_class.id == id, ids ) ) ) + return query class ToolsByUserListGrid( grids.Grid ): class NameColumn( grids.TextColumn ): @@ -117,7 +122,7 @@ if tool.categories: rval = '' for tca in tool.categories: - rval += '<a href="browse_category?id=%s">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name ) + rval += '<a href="browse_category?id=%s&webapp=community">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name ) return rval return 'not set' class StateColumn( grids.GridColumn ): @@ -148,7 +153,7 @@ return accepted_filters class UserColumn( grids.TextColumn ): def get_value( self, trans, grid, tool ): - return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username ) + return '<a href="browse_tools_by_user?operation=browse&id=%s&webapp=community">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username ) # Grid definition title = "Tools By User" model_class = model.Tool @@ -156,19 +161,24 @@ default_sort_key = "name" columns = [ NameColumn( "Name", - key="name", + # TODO: we cannot currently sort by columns since the grid may be filtered by tool ids + # and it is not clear if / how that will work. We need to be able to send to the grid helper + # the list of ids on which to filter when sorting on the column. + #key="name", model_class=model.Tool, link=( lambda item: dict( operation="View Tool", id=item.id, cntrller='tool', webapp="community" ) ), - attach_popup=True, - filterable="advanced" ), + attach_popup=True + #filterable="advanced" + ), VersionColumn( "Version", model_class=model.Tool, - attach_popup=False, - filterable="advanced" ), + attach_popup=False ), DescriptionColumn( "Description", - model_class=model.Tool, - attach_popup=False, - filterable="advanced" ), + #key="description", + model_class=model.Tool, + attach_popup=False + #filterable="advanced" + ), CategoryColumn( "Categories", model_class=model.Category, attach_popup=False, @@ -177,15 +187,20 @@ model_class=model.Event, attach_popup=False ), UserColumn( "Uploaded By", - key="username", + #key="username", model_class=model.User, - attach_popup=False, - filterable="advanced" ), + attach_popup=False + #filterable="advanced" + ), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn( "Deleted", + key="deleted", + visible=False, + filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", - cols_to_filter=[ columns[0], columns[1] ], + #cols_to_filter=[ columns[0], columns[2], columns[5] ], + cols_to_filter=[], key="free-text-search", visible=False, filterable="standard" ) ) @@ -199,30 +214,24 @@ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) ] - default_filter = dict( name="All", deleted="False", username="All" ) + default_filter = dict( name="All", deleted="False" ) num_rows_per_page = 50 preserve_state = False use_paging = True def build_initial_query( self, session ): return session.query( self.model_class ) def apply_default_filter( self, trans, query, **kwd ): - def filter_query( query, tool_id ): - if str( tool_id ).lower() in [ '', 'none' ]: - # Return an empty query since the current user cannot view any - # tools (possibly due to state not being approved, etc). - return query.filter( model.Tool.id == None ) - tool_id = util.listify( tool_id ) - query = query.filter( or_( *map( lambda id: self.model_class.id == id, tool_id ) ) ) - return query.filter( self.model_class.deleted==False ) - tool_id = kwd.get( 'tool_id', False ) - if not tool_id: + ids = kwd.get( 'ids', False ) + if not ids: # Display only approved tools - tool_id = get_approved_tools( trans ) - if not tool_id: - tool_id = 'None' - return filter_query( query, tool_id ) + ids = get_approved_tools( trans ) + if not ids or str( ids ).lower() == 'none': + return query.filter( trans.model.Tool.id == None ) + ids = util.listify( ids ) + query = query.filter( or_( *map( lambda id: self.model_class.id == id, ids ) ) ) + return query -class CategoryListGrid( grids.Grid ): +class ToolsByCategoryListGrid( grids.Grid ): class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, category ): return category.name @@ -242,28 +251,36 @@ # Grid definition webapp = "community" - title = "Tool Categories" + title = "Tools by Category" model_class = model.Category template='/webapps/community/category/grid.mako' default_sort_key = "name" columns = [ NameColumn( "Name", - key="name", + # TODO: we cannot currently sort by columns since the grid may be filtered by tool ids + # and it is not clear if / how that will work. We need to be able to send to the grid helper + # the list of ids on which to filter when sorting on the column. + #key="name", model_class=model.Category, link=( lambda item: dict( operation="Browse Category", id=item.id, webapp="community" ) ), - attach_popup=False, - filterable="advanced" ), + attach_popup=False + #filterable="advanced" + ), DescriptionColumn( "Description", - key="description", - model_class=model.Category, - attach_popup=False, - filterable="advanced" ), + #key="description", + model_class=model.Category, + attach_popup=False + #filterable="advanced" + ), ToolsColumn( "Tools", model_class=model.Tool, attach_popup=False, filterable="advanced" ), # Columns that are valid for filtering but are not visible. - grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + grids.DeletedColumn( "Deleted", + key="deleted", + visible=False, + filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", cols_to_filter=[ columns[0], columns[1] ], @@ -280,14 +297,12 @@ use_paging = True def build_initial_query( self, session ): return session.query( self.model_class ) - def apply_default_filter( self, trans, query, **kwd ): - return query.filter( self.model_class.deleted==False ) class ToolController( BaseController ): tool_list_grid = ToolListGrid() tools_by_user_list_grid = ToolsByUserListGrid() - category_list_grid = CategoryListGrid() + tools_by_category_list_grid = ToolsByCategoryListGrid() @web.expose def index( self, trans, **kwd ): @@ -312,7 +327,7 @@ cntrller='tool', **kwd ) ) # Render the list view - return self.category_list_grid( trans, **kwd ) + return self.tools_by_category_list_grid( trans, **kwd ) @web.expose def browse_category( self, trans, **kwd ): return trans.response.send_redirect( web.url_for( controller='common', diff -r 68fc85a43bb8 -r c7607fba91b9 lib/galaxy/webapps/community/controllers/upload.py --- a/lib/galaxy/webapps/community/controllers/upload.py Tue May 04 14:19:44 2010 -0400 +++ b/lib/galaxy/webapps/community/controllers/upload.py Tue May 04 14:21:21 2010 -0400 @@ -60,28 +60,26 @@ obj = datatype.create_model_object( meta ) trans.sa_session.add( obj ) if isinstance( obj, trans.app.model.Tool ): - existing = trans.sa_session.query( trans.app.model.Tool ).filter_by( tool_id = meta.id ).all() + existing = trans.sa_session.query( trans.app.model.Tool ).filter_by( tool_id = meta.id ).first() if existing and replace_id is None: raise UploadError( 'A tool with the same ID already exists. If you are trying to update this tool to a new version, please use the upload form on the "Edit Tool" page. Otherwise, please choose a new ID.' ) elif existing: - replace_version = trans.sa_session.query( trans.app.model.Tool ).get( int( trans.app.security.decode_id( replace_id ) ) ) + replace_version = trans.sa_session.query( trans.app.model.Tool ).get( trans.security.decode_id( replace_id ) ) if replace_version.newer_version: # If the user has picked an old version, switch to the newest version replace_version = get_versions( trans, replace_version )[0] - if trans.user != replace_version.user: - raise UploadError( 'You are not the owner of this tool and may not upload new versions of it.' ) if replace_version.tool_id != meta.id: - raise UploadError( 'The new tool id (%s) does not match the old tool id (%s). Please check the tool XML file' % ( meta.id, replace_version.tool_id ) ) + raise UploadError( 'The new tool id (%s) does not match the old tool id (%s). Check the tool XML file' % ( meta.id, replace_version.tool_id ) ) for old_version in get_versions( trans, replace_version ): if old_version.version == meta.version: - raise UploadError( 'The new version (%s) matches an old version. Please check your version in the tool XML file' % meta.version ) + raise UploadError( 'The new version (%s) matches an old version. Check your version in the tool XML file' % meta.version ) if old_version.is_new(): - raise UploadError( 'There is an existing version of this tool which is unsubmitted. Please either <a href="%s">submit or delete it</a> before uploading a new version.' % url_for( controller='common', - action='view_tool', - cntrller='tool', - id=trans.app.security.encode_id( old_version.id ) ) ) + raise UploadError( 'There is an existing version of this tool which has not yet been submitted for approval, so either <a href="%s">submit or delete it</a> before uploading a new version.' % url_for( controller='common', + action='view_tool', + cntrller='tool', + id=trans.security.encode_id( old_version.id ) ) ) if old_version.is_waiting(): - raise UploadError( 'There is an existing version of this tool which is waiting for administrative approval. Please contact an administrator for help.' ) + raise UploadError( 'There is an existing version of this tool which is waiting for administrative approval, so contact an administrator for help.' ) # Defer setting the id since the newer version id doesn't exist until the new Tool object is flushed if category_ids: for category_id in category_ids: @@ -91,12 +89,15 @@ trans.sa_session.add( tca ) # Initialize the tool event event = trans.app.model.Event( state=trans.app.model.Tool.states.NEW ) + # Flush to get an event id + trans.sa_session.add( event ) + trans.sa_session.flush() tea = trans.app.model.ToolEventAssociation( obj, event ) - trans.sa_session.add_all( ( event, tea ) ) - trans.sa_session.flush() + trans.sa_session.add( tea ) if replace_version and replace_id: replace_version.newer_version_id = obj.id - trans.sa_session.flush() + trans.sa_session.add( replace_version ) + trans.sa_session.flush() try: os.link( uploaded_file.name, obj.file_name ) except OSError: @@ -117,10 +118,10 @@ old_version = None for old_version in get_versions( trans, replace_version ): if old_version.is_new(): - message = 'There is an existing version of this tool which is unsubmitted. Please either submit or delete it before uploading a new version.' + message = 'There is an existing version of this tool which has not been submitted for approval, so either submit or delete it before uploading a new version.' break if old_version.is_waiting(): - message = 'There is an existing version of this tool which is waiting for administrative approval. Please contact an administrator for help.' + message = 'There is an existing version of this tool which is waiting for administrative approval, so contact an administrator for help.' break else: old_version = None diff -r 68fc85a43bb8 -r c7607fba91b9 lib/galaxy/webapps/community/model/__init__.py --- a/lib/galaxy/webapps/community/model/__init__.py Tue May 04 14:19:44 2010 -0400 +++ b/lib/galaxy/webapps/community/model/__init__.py Tue May 04 14:21:21 2010 -0400 @@ -94,7 +94,8 @@ APPROVED = 'approved', REJECTED = 'rejected', ARCHIVED = 'archived' ) - def __init__( self, guid=None, tool_id=None, name=None, description=None, user_description=None, category=None, version=None, user_id=None, external_filename=None ): + def __init__( self, guid=None, tool_id=None, name=None, description=None, user_description=None, + category=None, version=None, user_id=None, external_filename=None ): self.guid = guid self.tool_id = tool_id self.name = name or "Unnamed tool" @@ -103,6 +104,7 @@ self.version = version or "1.0.0" self.user_id = user_id self.external_filename = external_filename + self.deleted = False self.__extension = None def get_file_name( self ): if not self.external_filename: diff -r 68fc85a43bb8 -r c7607fba91b9 templates/dataset/display_application/display.mako --- a/templates/dataset/display_application/display.mako Tue May 04 14:19:44 2010 -0400 +++ b/templates/dataset/display_application/display.mako Tue May 04 14:21:21 2010 -0400 @@ -1,8 +1,8 @@ <%inherit file="/base.mako"/> <%namespace file="/message.mako" import="render_msg" /> <%def name="title()">Display Application: ${display_link.link.display_application.name} ${display_link.link.name}</%def> -%for message, message_type in msg: - ${render_msg( message, message_type )} +%for message, status in msg: + ${render_msg( message, status )} %endfor %if refresh: <%def name="metas()"><meta http-equiv="refresh" content="10" /></%def> diff -r 68fc85a43bb8 -r c7607fba91b9 templates/display_common.mako --- a/templates/display_common.mako Tue May 04 14:19:44 2010 -0400 +++ b/templates/display_common.mako Tue May 04 14:21:21 2010 -0400 @@ -129,10 +129,10 @@ </%def> ## Render message. -<%def name="render_message( message, message_type )"> +<%def name="render_message( message, status )"> %if message: <p> - <div class="${message_type}message transient-message">${util.restore_text( message )}</div> + <div class="${status}message transient-message">${util.restore_text( message )}</div> <div style="clear: both"></div> </p> %endif diff -r 68fc85a43bb8 -r c7607fba91b9 templates/grid_base.mako --- a/templates/grid_base.mako Tue May 04 14:19:44 2010 -0400 +++ b/templates/grid_base.mako Tue May 04 14:21:21 2010 -0400 @@ -508,12 +508,10 @@ webapp = href_parms[index].split('=')[1]; } } - // Do operation. do_operation(webapp, operation, id); return false; } - } // Navigate window to the URL defined by url_args. This method should be used to short-circuit grid AJAXing. @@ -678,7 +676,7 @@ <tr> <td width="75%">${self.render_grid_header( grid )}</td> <td></td> - <td width="25%" id="grid-message" valign="top">${render_message( message, message_type )}</td> + <td width="25%" id="grid-message" valign="top">${render_message( message, status )}</td> </tr> </table> @@ -774,10 +772,6 @@ ## Render grid table body contents. <%def name="render_grid_table_body_contents(grid, show_item_checkboxes=False)"> - ## Include the webapp value in the form - <td style="width: 1.5em;"> - <input type="hidden" name="webapp" value="${webapp}" /> - </td> <% num_rows_rendered = 0 %> %if query.count() == 0: ## No results. @@ -801,12 +795,8 @@ %for column in grid.columns: %if column.visible: <% - # Get filter params for generating filter links - filter_params = {} - for k, v in cur_filter_dict.items(): - filter_params['f-' + k] = v # Link - link = column.get_link( trans, grid, item, filter_params ) + link = column.get_link( trans, grid, item ) if link: href = url( **link ) else: @@ -831,6 +821,7 @@ cls = "menubutton" if column.attach_popup and href: cls = "menubutton split" + %> %if href: <td><div id="${id}" class="${cls}" style="float: left;"><a class="label" href="${href}">${v}</a></div></td> @@ -915,3 +906,4 @@ </tr> %endif </%def> + diff -r 68fc85a43bb8 -r c7607fba91b9 templates/grid_base_async.mako --- a/templates/grid_base_async.mako Tue May 04 14:19:44 2010 -0400 +++ b/templates/grid_base_async.mako Tue May 04 14:21:21 2010 -0400 @@ -13,4 +13,4 @@ ***** ${num_pages} ***** -${render_message( message, message_type )} \ No newline at end of file +${render_message( message, status )} \ No newline at end of file diff -r 68fc85a43bb8 -r c7607fba91b9 templates/message.mako --- a/templates/message.mako Tue May 04 14:19:44 2010 -0400 +++ b/templates/message.mako Tue May 04 14:21:21 2010 -0400 @@ -66,21 +66,21 @@ ## <%def name="center_panel()"> - ${render_large_message( message, message_type )} + ${render_large_message( message, status )} </%def> <%def name="body()"> - ${render_large_message( message, message_type )} + ${render_large_message( message, status )} </%def> ## Render large message. -<%def name="render_large_message( message, message_type )"> - <div class="${message_type}messagelarge" style="margin: 1em">${_(message)}</div> +<%def name="render_large_message( message, status )"> + <div class="${status}messagelarge" style="margin: 1em">${_(message)}</div> </%def> ## Render a message -<%def name="render_msg( msg, messagetype='done' )"> - <div class="${messagetype}message">${_(msg)}</div> +<%def name="render_msg( msg, status='done' )"> + <div class="${status}message">${_(msg)}</div> <br/> </%def> diff -r 68fc85a43bb8 -r c7607fba91b9 templates/page/select_items_grid_async.mako --- a/templates/page/select_items_grid_async.mako Tue May 04 14:19:44 2010 -0400 +++ b/templates/page/select_items_grid_async.mako Tue May 04 14:21:21 2010 -0400 @@ -6,4 +6,4 @@ ***** ${num_pages} ***** -${render_message( message, message_type )} \ No newline at end of file +${render_message( message, status )} \ No newline at end of file diff -r 68fc85a43bb8 -r c7607fba91b9 templates/webapps/community/admin/center.mako --- a/templates/webapps/community/admin/center.mako Tue May 04 14:19:44 2010 -0400 +++ b/templates/webapps/community/admin/center.mako Tue May 04 14:21:21 2010 -0400 @@ -6,7 +6,36 @@ <p>The menu on the left provides the following features</p> <ul> - <li><strong>Security</strong> - see the <strong>Data Security and Data Libraries</strong> section below for details + <li> + <strong>Tools</strong> + <p/> + <ul> + <li> + <strong>Tools awaiting approval</strong> + </li> + <p/> + <li> + <strong>Browse by category</strong> + </li> + <p/> + <li> + <strong>Browse all tools</strong> + </li> + <p/> + </ul> + </li> + <li> + <strong>Categories</strong> + <p/> + <ul> + <li> + <strong>Manage categories</strong> + </li> + <p/> + </ul> + </li> + <li> + <strong>Security</strong> <p/> <ul> <li> @@ -29,13 +58,5 @@ </ul> </li> <p/> - <li><strong>Tools</strong> - <p/> - <ul> - <li> - <strong>Manage tools</strong> - coming soon... - </li> - </ul> - </li> </ul> <br/> diff -r 68fc85a43bb8 -r c7607fba91b9 templates/webapps/community/tool/edit_tool.mako --- a/templates/webapps/community/tool/edit_tool.mako Tue May 04 14:19:44 2010 -0400 +++ b/templates/webapps/community/tool/edit_tool.mako Tue May 04 14:21:21 2010 -0400 @@ -51,16 +51,21 @@ ${render_msg( message, status )} %endif -%if cntrller == 'admin' or ( tool.is_new() and trans.user == tool.user ): +%if cntrller == 'admin' or trans.user == tool.user: <form id="edit_tool" name="edit_tool" action="${h.url_for( controller='common', action='edit_tool' )}" method="post"> <div class="toolForm"> <div class="toolFormTitle">${tool.name} - <a id="tool-${tool.id}-popup" class="popup-arrow" style="display: none;">▼</a> - <div popupmenu="tool-${tool.id}-popup"> - <a class="action-button" href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">View information</a> - <a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a> - <a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a> - </div> + %if not tool.deleted: + <a id="tool-${tool.id}-popup" class="popup-arrow" style="display: none;">▼</a> + <div popupmenu="tool-${tool.id}-popup"> + <a class="action-button" href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">View information</a> + <a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a> + <a class="action-button" href="${h.url_for( controller='common', action='delete_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Delete tool</a> + %if not tool.is_new() and not tool.is_waiting(): + <a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a> + %endif + </div> + %endif </div> <div class="toolFormBody"> <input type="hidden" name="id" value="${trans.app.security.encode_id( tool.id )}"/> diff -r 68fc85a43bb8 -r c7607fba91b9 templates/webapps/community/tool/view_tool.mako --- a/templates/webapps/community/tool/view_tool.mako Tue May 04 14:19:44 2010 -0400 +++ b/templates/webapps/community/tool/view_tool.mako Tue May 04 14:21:21 2010 -0400 @@ -85,16 +85,21 @@ <div class="toolForm"> <div class="toolFormTitle">${tool.name} - <a id="tool-${tool.id}-popup" class="popup-arrow" style="display: none;">▼</a> - <div popupmenu="tool-${tool.id}-popup"> - %if cntrller=='admin' or can_edit: - <a class="action-button" href="${h.url_for( controller='common', action='edit_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Edit information</a> - %endif - %if cntrller=='admin' or can_upload_new_version: - <a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a> - %endif - <a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a> - </div> + %if not tool.deleted: + <a id="tool-${tool.id}-popup" class="popup-arrow" style="display: none;">▼</a> + <div popupmenu="tool-${tool.id}-popup"> + %if cntrller=='admin' or can_edit: + <a class="action-button" href="${h.url_for( controller='common', action='edit_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Edit information</a> + %endif + <a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a> + %if cntrller=='admin' or trans.user==tool.user: + <a class="action-button" href="${h.url_for( controller='common', action='delete_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Delete tool</a> + %endif + %if cntrller=='admin' or can_upload_new_version: + <a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a> + %endif + </div> + %endif </div> <div class="toolFormBody"> <div class="form-row">