details: http://www.bx.psu.edu/hg/galaxy/rev/846f6a7ee80c changeset: 2556:846f6a7ee80c user: Greg Von Kuster <greg@bx.psu.edu> date: Wed Aug 12 15:56:03 2009 -0400 description: 1) Enhancements for sharing histories based on feedback from Assaf Gordon ( resolves ticket # 125 ): - Make "Histories shared with you by others" page a grid - Eliminate the option to clone deleted or all items for the user to which the history has been shared - Eliminate the "don't share" option when sharing histories - When the "share anyway" option is chosen, make sure there is something in "no_change_needed" dict 2) Fix library view template display for ldda information page ( resolves ticket 128 ) 13 file(s) affected in this change: lib/galaxy/web/controllers/admin.py lib/galaxy/web/controllers/history.py lib/galaxy/web/controllers/library.py lib/galaxy/web/framework/helpers/grids.py templates/admin/requests/grid.mako templates/history/grid.mako templates/history/list_shared.mako templates/history/options.mako templates/history/share.mako templates/history/shared_grid.mako templates/history/stored_grid.mako test/base/twilltestcase.py test/functional/test_history_functions.py diffs (1262 lines): diff -r 2630316ff75e -r 846f6a7ee80c lib/galaxy/web/controllers/admin.py --- a/lib/galaxy/web/controllers/admin.py Tue Aug 11 16:56:47 2009 -0400 +++ b/lib/galaxy/web/controllers/admin.py Wed Aug 12 15:56:03 2009 -0400 @@ -1214,7 +1214,6 @@ template = info_association.template # See if we have any field contents info = info_association.info - log.debug("####In library_dataset_dataset_association, info.content: %s" % str( info.content)) if info: field_contents = {} for index, value in enumerate( info.content ): diff -r 2630316ff75e -r 846f6a7ee80c lib/galaxy/web/controllers/history.py --- a/lib/galaxy/web/controllers/history.py Tue Aug 11 16:56:47 2009 -0400 +++ b/lib/galaxy/web/controllers/history.py Wed Aug 12 15:56:03 2009 -0400 @@ -12,7 +12,6 @@ # States for passing messages SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error" - class HistoryListGrid( grids.Grid ): # Custom column types @@ -70,8 +69,43 @@ def apply_default_filter( self, trans, query ): return query.filter_by( user=trans.user, purged=False ) +class SharedHistoryListGrid( grids.Grid ): + # Custom column types + class DatasetsByStateColumn( grids.GridColumn ): + def get_value( self, trans, grid, history ): + rval = [] + for state in ( 'ok', 'running', 'queued', 'error' ): + total = sum( 1 for d in history.active_datasets if d.state == state ) + if total: + rval.append( '<div class="count-box state-color-%s">%s</div>' % ( state, total ) ) + else: + rval.append( '' ) + return rval + class SharedByColumn( grids.GridColumn ): + def get_value( self, trans, grid, history ): + return history.user.email + # Grid definition + title = "Histories shared with you by others" + model_class = model.History + default_sort_key = "-update_time" + columns = [ + grids.GridColumn( "Name", key="name" ), + DatasetsByStateColumn( "Datasets (by state)", ncells=4 ), + grids.GridColumn( "Created", key="create_time", format=time_ago ), + grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), + SharedByColumn( "Shared by", key="user_id" ) + ] + operations = [ + grids.GridOperation( "Clone" ), + grids.GridOperation( "Unshare" ) + ] + standard_filters = [] + def build_initial_query( self, session ): + return session.query( self.model_class ).join( 'users_shared_with' ) + def apply_default_filter( self, trans, query ): + return query.filter( model.HistoryUserShareAssociation.user == trans.user ) + class HistoryController( BaseController ): - @web.expose def index( self, trans ): return "" @@ -80,7 +114,8 @@ """XML history list for functional tests""" return trans.fill_template( "/history/list_as_xml.mako" ) - list_grid = HistoryListGrid() + stored_list_grid = HistoryListGrid() + shared_list_grid = SharedHistoryListGrid() @web.expose @web.require_login( "work with multiple histories" ) @@ -91,7 +126,6 @@ if 'operation' in kwargs: history_ids = util.listify( kwargs.get( 'id', [] ) ) histories = [] - shared_by_others = [] operation = kwargs['operation'].lower() if operation == "share": return self.share( trans, **kwargs ) @@ -127,7 +161,7 @@ status, message = self._list_undelete( trans, histories ) trans.sa_session.flush() # Render the list view - return self.list_grid( trans, status=status, message=message, template='/history/grid.mako', **kwargs ) + return self.stored_list_grid( trans, status=status, message=message, template='/history/stored_grid.mako', **kwargs ) def _list_delete( self, trans, histories ): """Delete histories""" n_deleted = 0 @@ -195,18 +229,38 @@ # No message return None, None @web.expose - def list_shared( self, trans, **kwd ): + def list_shared( self, trans, **kwargs ): """List histories shared with current user by others""" - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - shared_by_others = trans.sa_session \ - .query( model.HistoryUserShareAssociation ) \ - .filter_by( user=trans.user ) \ - .join( 'history' ) \ - .filter( model.History.deleted == False ) \ - .order_by( desc( model.History.update_time ) ) \ - .all() - return trans.fill_template( "/history/list_shared.mako", shared_by_others=shared_by_others, msg=msg, messagetype='done' ) + msg = util.restore_text( kwargs.get( 'msg', '' ) ) + status = message = None + if 'operation' in kwargs: + id = kwargs.get( 'id', None ) + operation = kwargs['operation'].lower() + if operation == "clone": + if not id: + message = "Select a history to clone" + return self.shared_list_grid( trans, status='error', message=message, template='/history/shared_grid.mako', **kwargs ) + # When cloning shared histories, only copy active datasets + new_kwargs = { 'clone_choice' : 'active' } + return self.clone( trans, id, **new_kwargs ) + elif operation == 'unshare': + if not id: + message = "Select a history to unshare" + return self.shared_list_grid( trans, status='error', message=message, template='/history/shared_grid.mako', **kwargs ) + ids = util.listify( id ) + histories = [] + for history_id in ids: + history = get_history( trans, history_id, check_ownership=False ) + histories.append( history ) + for history in histories: + # Current user is the user with which the histories were shared + association = trans.app.model.HistoryUserShareAssociation.filter_by( user=trans.user, history=history ).one() + association.delete() + association.flush() + message = "Unshared %d shared histories" % len( ids ) + status = 'done' + # Render the list view + return self.shared_list_grid( trans, status=status, message=message, template='/history/shared_grid.mako', **kwargs ) @web.expose def delete_current( self, trans ): """Delete just the active history -- this does not require a logged in user.""" @@ -323,6 +377,9 @@ can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \ self._populate_restricted( trans, user, histories, send_to_users, None, send_to_err, unique=True ) send_to_err += err_msg + if cannot_change and not no_change_needed and not can_change: + send_to_err = "The histories you are sharing do not contain any datasets that can be accessed by the users with which you are sharing." + return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err ) if can_change or cannot_change: return trans.fill_template( "/history/share.mako", histories=histories, @@ -350,8 +407,6 @@ email=email, err_msg=err_msg, share_button=True ) ) - if action == "no_share": - trans.response.send_redirect( url_for( controller='root', action='history_options' ) ) user = trans.get_user() histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email ) send_to_err = '' @@ -629,29 +684,38 @@ @web.expose @web.require_login( "clone shared Galaxy history" ) def clone( self, trans, id, **kwd ): - history = get_history( trans, id, check_ownership=False ) + """Clone a list of histories""" params = util.Params( kwd ) + ids = util.listify( id ) + histories = [] + for history_id in ids: + history = get_history( trans, history_id, check_ownership=False ) + histories.append( history ) clone_choice = params.get( 'clone_choice', None ) if not clone_choice: return trans.fill_template( "/history/clone.mako", history=history ) user = trans.get_user() - if history.user == user: - owner = True + for history in histories: + if history.user == user: + owner = True + else: + if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \ + .filter_by( user=user, history=history ).count() == 0: + return trans.show_error_message( "The history you are attempting to clone is not owned by you or shared with you. " ) + owner = False + name = "Clone of '%s'" % history.name + if not owner: + name += " shared by '%s'" % history.user.email + if clone_choice == 'activatable': + new_history = history.copy( name=name, target_user=user, activatable=True ) + elif clone_choice == 'active': + name += " (active items only)" + new_history = history.copy( name=name, target_user=user ) + if len( histories ) == 1: + msg = 'Clone with name "%s" is now included in your previously stored histories.' % new_history.name else: - if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \ - .filter_by( user=user, history=history ).count() == 0: - return trans.show_error_message( "The history you are attempting to clone is not owned by you or shared with you. " ) - owner = False - name = "Clone of '%s'" % history.name - if not owner: - name += " shared by '%s'" % history.user.email - if clone_choice == 'activatable': - new_history = history.copy( name=name, target_user=user, activatable=True ) - elif clone_choice == 'active': - name += " (active items only)" - new_history = history.copy( name=name, target_user=user ) - # Render the list view - return trans.show_ok_message( 'Clone with name "%s" is now included in your list of stored histories.' % new_history.name ) + msg = '%d cloned histories are now included in your previously stored histories.' % len( histories ) + return trans.show_ok_message( msg ) ## ---- Utility methods ------------------------------------------------------- diff -r 2630316ff75e -r 846f6a7ee80c lib/galaxy/web/controllers/library.py --- a/lib/galaxy/web/controllers/library.py Tue Aug 11 16:56:47 2009 -0400 +++ b/lib/galaxy/web/controllers/library.py Wed Aug 12 15:56:03 2009 -0400 @@ -469,7 +469,7 @@ msg=util.sanitize_text( msg ), messagetype='error' ) ) # See if we have any associated templates - info_association = folder.get_info_association() + info_association = ldda.get_info_association() if info_association: template = info_association.template # See if we have any field contents diff -r 2630316ff75e -r 846f6a7ee80c lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py Tue Aug 11 16:56:47 2009 -0400 +++ b/lib/galaxy/web/framework/helpers/grids.py Wed Aug 12 15:56:03 2009 -0400 @@ -156,9 +156,7 @@ elif column_filter == "All": del filter_args[self.key] return query - - - + class GridOperation( object ): def __init__( self, label, key=None, condition=None, allow_multiple=True ): self.label = label diff -r 2630316ff75e -r 846f6a7ee80c templates/admin/requests/grid.mako --- a/templates/admin/requests/grid.mako Tue Aug 11 16:56:47 2009 -0400 +++ b/templates/admin/requests/grid.mako Wed Aug 12 15:56:03 2009 -0400 @@ -101,7 +101,7 @@ %if not len(query.all()): - There are no request(s). + There are no requests. %else: <form name="history_actions" action="${url()}" method="post" > <table class="grid"> diff -r 2630316ff75e -r 846f6a7ee80c templates/history/grid.mako --- a/templates/history/grid.mako Tue Aug 11 16:56:47 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,194 +0,0 @@ -<%inherit file="/base.mako"/> -<%def name="title()">${grid.title}</%def> - -%if message: - <p> - <div class="${message_type}message transient-message">${message}</div> - <div style="clear: both"></div> - </p> -%endif - -<%def name="javascripts()"> - ${parent.javascripts()} - <script type="text/javascript"> - ## TODO: generalize and move into galaxy.base.js - $(document).ready(function() { - $(".grid").each( function() { - var grid = this; - var checkboxes = $(this).find("input.grid-row-select-checkbox"); - var update = $(this).find( "span.grid-selected-count" ); - $(checkboxes).each( function() { - $(this).change( function() { - var n = $(checkboxes).filter("[checked]").size(); - update.text( n ); - }); - }) - }); - }); - ## Can this be moved into base.mako? - %if refresh_frames: - %if 'masthead' in refresh_frames: - ## Refresh masthead == user changes (backward compatibility) - if ( parent.user_changed ) { - %if trans.user: - parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} ); - %else: - parent.user_changed( null, false ); - %endif - } - %endif - %if 'history' in refresh_frames: - if ( parent.frames && parent.frames.galaxy_history ) { - parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}"; - if ( parent.force_right_panel ) { - parent.force_right_panel( 'show' ); - } - } - %endif - %if 'tools' in refresh_frames: - if ( parent.frames && parent.frames.galaxy_tools ) { - parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}"; - if ( parent.force_left_panel ) { - parent.force_left_panel( 'show' ); - } - } - %endif - %endif - </script> -</%def> - -<%def name="stylesheets()"> - <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" /> - <style> - ## Not generic to all grids -- move to base? - .count-box { - min-width: 1.1em; - padding: 5px; - border-width: 1px; - border-style: solid; - text-align: center; - display: inline-block; - } - </style> -</%def> - -<div class="grid-header"> - <h2>${grid.title}</h2> - <span class="title">Filter:</span> - %for i, filter in enumerate( grid.standard_filters ): - %if i > 0: - <span>|</span> - %endif - <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span> - %endfor -</div> - -<form name="history_actions" action="${url()}" method="post" > - <table class="grid"> - <thead> - <tr> - <th></th> - %for column in grid.columns: - %if column.visible: - <% - href = "" - extra = "" - if column.sortable: - if sort_key == column.key: - if sort_order == "asc": - href = url( sort=( "-" + column.key ) ) - extra = "↓" - else: - href = url( sort=( column.key ) ) - extra = "↑" - else: - href = url( sort=column.key ) - %> - <th\ - %if column.ncells > 1: - colspan="${column.ncells}" - %endif - > - %if href: - <a href="${href}">${column.label}</a> - %else: - ${column.label} - %endif - <span>${extra}</span> - </th> - %endif - %endfor - <th></th> - </tr> - </thead> - <tbody> - %for i, item in enumerate( query ): - <tr \ - %if current_item == item: - class="current" \ - %endif - > - ## Item selection column - <td style="width: 1.5em;"> - <input type="checkbox" name="id" value=${trans.security.encode_id( item.id )} class="grid-row-select-checkbox" /> - </td> - ## Data columns - %for column in grid.columns: - %if column.visible: - <% - # Link - link = column.get_link( trans, grid, item ) - if link: - href = url( **link ) - else: - href = None - # Value (coerced to list so we can loop) - value = column.get_value( trans, grid, item ) - if column.ncells == 1: - value = [ value ] - %> - %for cellnum, v in enumerate( value ): - <% - # Attach popup menu? - if column.attach_popup and cellnum == 0: - extra = '<a id="grid-%d-popup" class="popup-arrow" style="display: none;">▼</a>' % i - else: - extra = "" - %> - %if href: - <td><a href="${href}">${v}</a> ${extra}</td> - %else: - <td >${v}${extra}</td> - %endif - </td> - %endfor - %endif - %endfor - ## Actions column - <td> - <div popupmenu="grid-${i}-popup"> - %for operation in grid.operations: - %if operation.allowed( item ): - <a class="action-button" href="${url( operation=operation.label, id=item.id )}">${operation.label}</a> - %endif - %endfor - </div> - </td> - </tr> - %endfor - </tbody> - <tfoot> - <tr> - <td></td> - <td colspan="100"> - For <span class="grid-selected-count"></span> selected histories: - %for operation in grid.operations: - %if operation.allow_multiple: - <input type="submit" name="operation" value="${operation.label}" class="action-button"> - %endif - %endfor - </td> - </tr> - </tfoot> - </table> -</form> diff -r 2630316ff75e -r 846f6a7ee80c templates/history/list_shared.mako --- a/templates/history/list_shared.mako Tue Aug 11 16:56:47 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -<%inherit file="/base.mako"/> -<%namespace file="/message.mako" import="render_msg" /> - -%if msg: - ${render_msg( msg, messagetype )} -%endif - -%if shared_by_others: - <table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%"> - <tr class="header"> - <th>Name</th> - <th>Owner</th> - </tr> - %for i, association in enumerate( shared_by_others ): - <% history = association.history %> - <tr> - <td> - ${history.name} - <a id="shared-${i}-popup" class="popup-arrow" style="display: none;">▼</a> - <div popupmenu="shared-${i}-popup"> - <a class="action-button" href="${h.url_for( controller='history', action='clone', id=trans.security.encode_id( history.id ) )}">Clone</a> - </div> - </td> - <td>${history.user.email}</td> - </tr> - %endfor - </table> -%else: - No histories have been shared with you. -%endif diff -r 2630316ff75e -r 846f6a7ee80c templates/history/options.mako --- a/templates/history/options.mako Tue Aug 11 16:56:47 2009 -0400 +++ b/templates/history/options.mako Wed Aug 12 15:56:03 2009 -0400 @@ -12,7 +12,7 @@ <ul> %if user: - <li><a href="${h.url_for( controller='history', action='list')}" target="galaxy_main">List</a> previously stored histories</li> + <li><a href="${h.url_for( controller='history', action='list')}" target="galaxy_main">Previously</a> stored histories</li> %if len( history.active_datasets ) > 0: <li><a href="${h.url_for( controller='root', action='history_new' )}">Create</a> a new empty history</li> <li><a href="${h.url_for( controller='workflow', action='build_from_current_history' )}">Construct workflow</a> from current history</li> @@ -27,6 +27,6 @@ <li><a href="${h.url_for( controller='history', action='rename', id=trans.security.encode_id( history.id ) )}" target="galaxy_main">Rename</a> current history (stored as "${history.name}")</li> <li><a href="${h.url_for( controller='history', action='delete_current' )}" confirm="Are you sure you want to delete the current history?">Delete</a> current history</div> %if user and user.histories_shared_by_others: - <li><a href="${h.url_for( controller='history', action='list_shared')}" target="galaxy_main">List</a> histories shared with you by others</li> + <li><a href="${h.url_for( controller='history', action='list_shared')}" target="galaxy_main">Histories</a> shared with you by others</li> %endif </ul> diff -r 2630316ff75e -r 846f6a7ee80c templates/history/share.mako --- a/templates/history/share.mako Tue Aug 11 16:56:47 2009 -0400 +++ b/templates/history/share.mako Wed Aug 12 15:56:03 2009 -0400 @@ -57,134 +57,136 @@ </form> %else: ## We are sharing restricted histories - <form name='share_restricted' id=share_restricted' action="${h.url_for( controller='history', action='share_restricted' )}" method="post"> - %if send_to_err: - <div style="clear: both"></div> - <div class="form-row"> - <div class="errormessage">${send_to_err}</div> - </div> - %endif - ## Needed for rebuilding dicts - <input type="hidden" name="email" value="${email}" size="40"> - %for history in histories: - <input type="hidden" name="id" value="${trans.security.encode_id( history.id )}"> - %endfor - %if no_change_needed: - ## no_change_needed looks like: {historyX : [hda, hda], historyY : [hda] } - <div style="clear: both"></div> - <div class="form-row"> - <div class="donemessage"> - The following datasets can be shared with ${email} with no changes - </div> - </div> - %for history, hdas in no_change_needed.items(): - <div class="form-row"> - <label>History</label> - ${history.name} - </div> + %if no_change_needed or can_change: + <form name='share_restricted' id=share_restricted' action="${h.url_for( controller='history', action='share_restricted' )}" method="post"> + %if send_to_err: <div style="clear: both"></div> <div class="form-row"> - <label>Datasets</label> + <div class="errormessage">${send_to_err}</div> </div> - %for hda in hdas: - <div class="form-row"> - ${hda.name} - %if hda.deleted: - (deleted) - %endif - </div> - %endfor + %endif + ## Needed for rebuilding dicts + <input type="hidden" name="email" value="${email}" size="40"> + %for history in histories: + <input type="hidden" name="id" value="${trans.security.encode_id( history.id )}"> %endfor - %endif - %if can_change: - ## can_change looks like: {historyX : [hda, hda], historyY : [hda] } - <div style="clear: both"></div> - <div class="form-row"> - <div class="warningmessage"> - The following datasets can be shared with ${email} by updating their permissions - </div> - </div> - %for history, hdas in can_change.items(): - <div class="form-row"> - <label>History</label> - ${history.name} - </div> + %if no_change_needed: + ## no_change_needed looks like: {historyX : [hda, hda], historyY : [hda] } <div style="clear: both"></div> <div class="form-row"> - <label>Datasets</label> - </div> - %for hda in hdas: + <div class="donemessage"> + The following datasets can be shared with ${email} with no changes + </div> + </div> + %for history, hdas in no_change_needed.items(): <div class="form-row"> - ${hda.name} - %if hda.deleted: - (deleted) - %endif + <label>History</label> + ${history.name} </div> + <div style="clear: both"></div> + <div class="form-row"> + <label>Datasets</label> + </div> + %for hda in hdas: + <div class="form-row"> + ${hda.name} + %if hda.deleted: + (deleted) + %endif + </div> + %endfor %endfor - %endfor - %endif - %if cannot_change: - ## cannot_change looks like: {historyX : [hda, hda], historyY : [hda] } - <div style="clear: both"></div> - <div class="form-row"> - <div class="errormessage"> - The following datasets cannot be shared with ${email} because you are not authorized to - change the permissions on them - </div> - </div> - %for history, hdas in cannot_change.items(): - <div class="form-row"> - <label>History</label> - ${history.name} - </div> + %endif + %if can_change: + ## can_change looks like: {historyX : [hda, hda], historyY : [hda] } <div style="clear: both"></div> <div class="form-row"> - <label>Datasets</label> + <div class="warningmessage"> + The following datasets can be shared with ${email} by updating their permissions + </div> </div> - %for hda in hdas: + %for history, hdas in can_change.items(): <div class="form-row"> - ${hda.name} - %if hda.deleted: - (deleted) - %endif + <label>History</label> + ${history.name} </div> + <div style="clear: both"></div> + <div class="form-row"> + <label>Datasets</label> + </div> + %for hda in hdas: + <div class="form-row"> + ${hda.name} + %if hda.deleted: + (deleted) + %endif + </div> + %endfor %endfor - %endfor - %endif - <div class="toolFormTitle"></div> - <div class="form-row"> - Deleted items can be eliminated by the users with which you are sharing the history. - </div> - <div class="form-row"> - <label>How would you like to proceed?</label> - </div> - %if can_change: + %endif + %if cannot_change: + ## cannot_change looks like: {historyX : [hda, hda], historyY : [hda] } + <div style="clear: both"></div> + <div class="form-row"> + <div class="errormessage"> + The following datasets cannot be shared with ${email} because you are not authorized to + change the permissions on them + </div> + </div> + %for history, hdas in cannot_change.items(): + <div class="form-row"> + <label>History</label> + ${history.name} + </div> + <div style="clear: both"></div> + <div class="form-row"> + <label>Datasets</label> + </div> + %for hda in hdas: + <div class="form-row"> + ${hda.name} + %if hda.deleted: + (deleted) + %endif + </div> + %endfor + %endfor + %endif + <div class="toolFormTitle"></div> <div class="form-row"> - <input type="radio" name="action" value="public"> Make datasets public so anyone can access them - %if cannot_change: - (where possible) - %endif + <label>How would you like to proceed?</label> </div> + %if can_change: + <div class="form-row"> + <input type="radio" name="action" value="public"> Make datasets public so anyone can access them + %if cannot_change: + (where possible) + %endif + </div> + <div class="form-row"> + %if no_change_needed: + <input type="radio" name="action" value="private"> Make datasets private to me and the user(s) with whom I am sharing + %else: + <input type="radio" name="action" value="private" checked> Make datasets private to me and the user(s) with whom I am sharing + %endif + %if cannot_change: + (where possible) + %endif + </div> + %endif + %if no_change_needed: + <div class="form-row"> + <input type="radio" name="action" value="share_anyway" checked> Share anyway + %if can_change: + (don't change any permissions) + %endif + </div> + %endif <div class="form-row"> - <input type="radio" name="action" value="private"> Make datasets private to me and the user(s) with whom I am sharing - %if cannot_change: - (where possible) - %endif + <input type="submit" name="share_restricted_button" value="Go"><br/> </div> - %endif - <div class="form-row"> - <input type="radio" name="action" value="share_anyway"> Share anyway - %if can_change: - (don't change any permissions) - %endif - </div> - <div class="form-row"> - <input type="radio" name="action" value="no_share" checked> Don't share - </div> - <div class="form-row"> - <input type="submit" name="share_restricted_button" value="Go"><br/> - </div> - </form> + </form> + %endif %endif </div> </div> diff -r 2630316ff75e -r 846f6a7ee80c templates/history/shared_grid.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/history/shared_grid.mako Wed Aug 12 15:56:03 2009 -0400 @@ -0,0 +1,197 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="title()">${grid.title}</%def> + +<%def name="javascripts()"> + ${parent.javascripts()} + <script type="text/javascript"> + ## TODO: generalize and move into galaxy.base.js + $(document).ready(function() { + $(".grid").each( function() { + var grid = this; + var checkboxes = $(this).find("input.grid-row-select-checkbox"); + var update = $(this).find( "span.grid-selected-count" ); + $(checkboxes).each( function() { + $(this).change( function() { + var n = $(checkboxes).filter("[checked]").size(); + update.text( n ); + }); + }) + }); + }); + ## Can this be moved into base.mako? + %if refresh_frames: + %if 'masthead' in refresh_frames: + ## Refresh masthead == user changes (backward compatibility) + if ( parent.user_changed ) { + %if trans.user: + parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} ); + %else: + parent.user_changed( null, false ); + %endif + } + %endif + %if 'history' in refresh_frames: + if ( parent.frames && parent.frames.galaxy_history ) { + parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}"; + if ( parent.force_right_panel ) { + parent.force_right_panel( 'show' ); + } + } + %endif + %if 'tools' in refresh_frames: + if ( parent.frames && parent.frames.galaxy_tools ) { + parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}"; + if ( parent.force_left_panel ) { + parent.force_left_panel( 'show' ); + } + } + %endif + %endif + </script> +</%def> + +<%def name="stylesheets()"> + <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" /> + <style> + ## Not generic to all grids -- move to base? + .count-box { + min-width: 1.1em; + padding: 5px; + border-width: 1px; + border-style: solid; + text-align: center; + display: inline-block; + } + </style> +</%def> + +%if grid.standard_filters: + <div class="grid-header"> + <h2>${grid.title}</h2> + <span class="title">Filter:</span> + %for i, filter in enumerate( grid.standard_filters ): + %if i > 0: + <span>|</span> + %endif + <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span> + %endfor + </div> +%endif + +%if message: + <p> + <div class="${message_type}message transient-message">${message}</div> + <div style="clear: both"></div> + </p> +%endif +%if msg: + ${render_msg( msg, messagetype )} +%endif + +<form name="history_shared_by_others" action="${url()}" method="post" > + <table class="grid"> + <thead> + <tr> + <th></th> + %for column in grid.columns: + %if column.visible: + <% + href = "" + extra = "" + if column.sortable: + if sort_key == column.key: + if sort_order == "asc": + href = url( sort=( "-" + column.key ) ) + extra = "↓" + else: + href = url( sort=( column.key ) ) + extra = "↑" + else: + href = url( sort=column.key ) + %> + <th\ + %if column.ncells > 1: + colspan="${column.ncells}" + %endif + > + %if href: + <a href="${href}">${column.label}</a> + %else: + ${column.label} + %endif + <span>${extra}</span> + </th> + %endif + %endfor + <th></th> + </tr> + </thead> + <tbody> + %for i, history in enumerate( query ): + <tr> + ## Item selection column + <td style="width: 1.5em;"> + <input type="checkbox" name="id" value=${trans.security.encode_id( history.id )} class="grid-row-select-checkbox" /> + </td> + ## Data columns + %for column in grid.columns: + %if column.visible: + <% + # Link + link = column.get_link( trans, grid, history ) + if link: + href = url( **link ) + else: + href = None + # Value (coerced to list so we can loop) + value = column.get_value( trans, grid, history ) + if column.ncells == 1: + value = [ value ] + %> + %for cellnum, v in enumerate( value ): + <% + # Attach popup menu? + if column.attach_popup and cellnum == 0: + extra = '<a id="grid-%d-popup" class="popup-arrow" style="display: none;">▼</a>' % i + else: + extra = "" + %> + %if href: + <td><a href="${href}">${v}</a> ${extra}</td> + %else: + <td >${v}${extra}</td> + %endif + </td> + %endfor + %endif + %endfor + ## Actions column + <td> + <div popupmenu="grid-${i}-popup"> + %for operation in grid.operations: + %if operation.allowed( history ): + <a class="action-button" href="${url( operation=operation.label, id=history.id )}">${operation.label}</a> + %endif + %endfor + </div> + </td> + </tr> + %endfor + </tbody> + <tfoot> + <tr> + <td></td> + <td colspan="100"> + For <span class="grid-selected-count"></span> selected histories: + %for operation in grid.operations: + %if operation.allow_multiple: + <input type="submit" name="operation" value="${operation.label}" class="action-button"> + %endif + %endfor + </td> + </tr> + </tfoot> + </table> +</form> diff -r 2630316ff75e -r 846f6a7ee80c templates/history/stored_grid.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/history/stored_grid.mako Wed Aug 12 15:56:03 2009 -0400 @@ -0,0 +1,196 @@ +<%inherit file="/base.mako"/> +<%def name="title()">${grid.title}</%def> + +%if message: + <p> + <div class="${message_type}message transient-message">${message}</div> + <div style="clear: both"></div> + </p> +%endif + +<%def name="javascripts()"> + ${parent.javascripts()} + <script type="text/javascript"> + ## TODO: generalize and move into galaxy.base.js + $(document).ready(function() { + $(".grid").each( function() { + var grid = this; + var checkboxes = $(this).find("input.grid-row-select-checkbox"); + var update = $(this).find( "span.grid-selected-count" ); + $(checkboxes).each( function() { + $(this).change( function() { + var n = $(checkboxes).filter("[checked]").size(); + update.text( n ); + }); + }) + }); + }); + ## Can this be moved into base.mako? + %if refresh_frames: + %if 'masthead' in refresh_frames: + ## Refresh masthead == user changes (backward compatibility) + if ( parent.user_changed ) { + %if trans.user: + parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} ); + %else: + parent.user_changed( null, false ); + %endif + } + %endif + %if 'history' in refresh_frames: + if ( parent.frames && parent.frames.galaxy_history ) { + parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}"; + if ( parent.force_right_panel ) { + parent.force_right_panel( 'show' ); + } + } + %endif + %if 'tools' in refresh_frames: + if ( parent.frames && parent.frames.galaxy_tools ) { + parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}"; + if ( parent.force_left_panel ) { + parent.force_left_panel( 'show' ); + } + } + %endif + %endif + </script> +</%def> + +<%def name="stylesheets()"> + <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" /> + <style> + ## Not generic to all grids -- move to base? + .count-box { + min-width: 1.1em; + padding: 5px; + border-width: 1px; + border-style: solid; + text-align: center; + display: inline-block; + } + </style> +</%def> + +%if grid.standard_filters: + <div class="grid-header"> + <h2>${grid.title}</h2> + <span class="title">Filter:</span> + %for i, filter in enumerate( grid.standard_filters ): + %if i > 0: + <span>|</span> + %endif + <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span> + %endfor + </div> +%endif + +<form name="history_actions" action="${url()}" method="post" > + <table class="grid"> + <thead> + <tr> + <th></th> + %for column in grid.columns: + %if column.visible: + <% + href = "" + extra = "" + if column.sortable: + if sort_key == column.key: + if sort_order == "asc": + href = url( sort=( "-" + column.key ) ) + extra = "↓" + else: + href = url( sort=( column.key ) ) + extra = "↑" + else: + href = url( sort=column.key ) + %> + <th\ + %if column.ncells > 1: + colspan="${column.ncells}" + %endif + > + %if href: + <a href="${href}">${column.label}</a> + %else: + ${column.label} + %endif + <span>${extra}</span> + </th> + %endif + %endfor + <th></th> + </tr> + </thead> + <tbody> + %for i, item in enumerate( query ): + <tr \ + %if current_item == item: + class="current" \ + %endif + > + ## Item selection column + <td style="width: 1.5em;"> + <input type="checkbox" name="id" value=${trans.security.encode_id( item.id )} class="grid-row-select-checkbox" /> + </td> + ## Data columns + %for column in grid.columns: + %if column.visible: + <% + # Link + link = column.get_link( trans, grid, item ) + if link: + href = url( **link ) + else: + href = None + # Value (coerced to list so we can loop) + value = column.get_value( trans, grid, item ) + if column.ncells == 1: + value = [ value ] + %> + %for cellnum, v in enumerate( value ): + <% + # Attach popup menu? + if column.attach_popup and cellnum == 0: + extra = '<a id="grid-%d-popup" class="popup-arrow" style="display: none;">▼</a>' % i + else: + extra = "" + %> + %if href: + <td><a href="${href}">${v}</a> ${extra}</td> + %else: + <td >${v}${extra}</td> + %endif + </td> + %endfor + %endif + %endfor + ## Actions column + <td> + <div popupmenu="grid-${i}-popup"> + %for operation in grid.operations: + %if operation.allowed( item ): + <a class="action-button" href="${url( operation=operation.label, id=item.id )}">${operation.label}</a> + %endif + %endfor + </div> + </td> + </tr> + %endfor + </tbody> + <tfoot> + <tr> + <td></td> + <td colspan="100"> + For <span class="grid-selected-count"></span> selected histories: + %for operation in grid.operations: + %if operation.allow_multiple: + <input type="submit" name="operation" value="${operation.label}" class="action-button"> + %endif + %endfor + </td> + </tr> + </tfoot> + </table> +</form> diff -r 2630316ff75e -r 846f6a7ee80c test/base/twilltestcase.py --- a/test/base/twilltestcase.py Tue Aug 11 16:56:47 2009 -0400 +++ b/test/base/twilltestcase.py Wed Aug 12 15:56:03 2009 -0400 @@ -182,7 +182,7 @@ self.home() self.visit_page( "root/history_options" ) if user: - self.check_page_for_string( 'List</a> previously stored histories' ) + self.check_page_for_string( 'Previously</a> stored histories' ) if active_datasets: self.check_page_for_string( 'Create</a> a new empty history' ) self.check_page_for_string( 'Construct workflow</a> from current history' ) @@ -190,7 +190,7 @@ self.check_page_for_string( 'Share</a> current history' ) self.check_page_for_string( 'Change default permissions</a> for current history' ) if histories_shared_by_others: - self.check_page_for_string( 'List</a> histories shared with you by others' ) + self.check_page_for_string( 'Histories</a> shared with you by others' ) if activatable_datasets: self.check_page_for_string( 'Show deleted</a> datasets in current history' ) self.check_page_for_string( 'Rename</a> current history' ) diff -r 2630316ff75e -r 846f6a7ee80c test/functional/test_history_functions.py --- a/test/functional/test_history_functions.py Tue Aug 11 16:56:47 2009 -0400 +++ b/test/functional/test_history_functions.py Wed Aug 12 15:56:03 2009 -0400 @@ -183,7 +183,7 @@ self.view_shared_histories( check_str=history3.name, check_str2=admin_user.email ) self.clone_history( self.security.encode_id( history3.id ), 'activatable', - check_str_after_submit='is now included in your list of stored histories.' ) + check_str_after_submit='is now included in your previously stored histories.' ) global history3_clone1 history3_clone1 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False, galaxy.model.History.table.c.user_id==regular_user1.id ) ) \ @@ -216,7 +216,7 @@ # Test cloning activatable datasets self.clone_history( self.security.encode_id( history3.id ), 'activatable', - check_str_after_submit='is now included in your list of stored histories.' ) + check_str_after_submit='is now included in your previously stored histories.' ) global history3_clone2 history3_clone2 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False, galaxy.model.History.table.c.user_id==admin_user.id ) ) \ @@ -244,7 +244,7 @@ # Test cloning only active datasets self.clone_history( self.security.encode_id( history3.id ), 'active', - check_str_after_submit='is now included in your list of stored histories.' ) + check_str_after_submit='is now included in your previously stored histories.' ) global history3_clone3 history3_clone3 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False, galaxy.model.History.table.c.user_id==admin_user.id ) ) \ @@ -355,7 +355,7 @@ # Clone restricted history5 self.clone_history( self.security.encode_id( history5.id ), 'activatable', - check_str_after_submit='is now included in your list of stored histories.' ) + check_str_after_submit='is now included in your previously stored histories.' ) global history5_clone1 history5_clone1 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False, galaxy.model.History.table.c.user_id==regular_user1.id ) ) \ @@ -411,7 +411,7 @@ # Clone restricted history5 self.clone_history( self.security.encode_id( history5.id ), 'activatable', - check_str_after_submit='is now included in your list of stored histories.' ) + check_str_after_submit='is now included in your previously stored histories.' ) global history5_clone2 history5_clone2 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False, galaxy.model.History.table.c.user_id==regular_user2.id ) ) \ @@ -484,7 +484,7 @@ # Clone restricted history5 self.clone_history( self.security.encode_id( history5.id ), 'activatable', - check_str_after_submit='is now included in your list of stored histories.' ) + check_str_after_submit='is now included in your previously stored histories.' ) global history5_clone3 history5_clone3 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False, galaxy.model.History.table.c.user_id==regular_user2.id ) ) \ @@ -522,7 +522,7 @@ # Clone restricted history5 self.clone_history( self.security.encode_id( history5.id ), 'activatable', - check_str_after_submit='is now included in your list of stored histories.' ) + check_str_after_submit='is now included in your previously stored histories.' ) global history5_clone4 history5_clone4 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False, galaxy.model.History.table.c.user_id==regular_user3.id ) ) \ @@ -591,15 +591,6 @@ pass self.logout() self.login( email=admin_user.email ) - email = '%s,%s' % ( regular_user2.email, regular_user3.email ) - check_str_after_submit = 'The following datasets can be shared with %s with no changes' % email - check_str_after_submit2 = 'The following datasets can be shared with %s by updating their permissions' % email - action_check_str_after_submit = 'History Options' - self.share_current_history( email, - check_str_after_submit=check_str_after_submit, - check_str_after_submit2=check_str_after_submit2, - action='no_share', - action_check_str_after_submit=action_check_str_after_submit ) def test_070_history_show_and_hide_deleted_datasets( self ): """Testing displaying deleted history items""" # Logged in as admin_user