Streamline history sharing, add a "Manage shared histories" section to the History options menu. Also use a better approach to setting peek on datasets.

This commit is contained in:
Greg Von Kuster
2009-09-10 21:24:06 -04:00
parent 1a2bd47618
commit 3eab20f513
5 changed files with 157 additions and 118 deletions
+4 -4
View File
@@ -416,8 +416,9 @@ def get_file_peek( file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5 ):
count = 0
file_type = None
data_checked = False
for line in file( file_name ):
line = line[:WIDTH]
temp = open( file_name, "U" )
while count <= LINE_COUNT:
line = temp.readline( WIDTH )
if line and not is_multi_byte and not data_checked:
# See if we have a compressed or binary file
if line[0:2] == util.gzip_magic:
@@ -432,9 +433,8 @@ def get_file_peek( file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5 ):
if file_type in [ 'gzipped', 'binary' ]:
break
lines.append( line )
if count == LINE_COUNT:
break
count += 1
temp.close()
if file_type in [ 'gzipped', 'binary' ]:
text = "%s file" % file_type
else:
+80 -26
View File
@@ -4,7 +4,7 @@ from galaxy import util
from galaxy.model.mapping import desc
from galaxy.model.orm import *
from galaxy.util.json import *
import webhelpers, logging
import webhelpers, logging, operator
from datetime import datetime
from cgi import escape
@@ -31,10 +31,12 @@ class HistoryListGrid( grids.Grid ):
return "deleted"
elif history.users_shared_with:
return "shared"
elif history.importable:
return "importable"
return ""
def get_link( self, trans, grid, item ):
if item.users_shared_with:
return dict( operation="sharing", id=item.id )
if item.users_shared_with or item.importable:
return dict( operation="sharing" )
return None
# Grid definition
title = "Stored histories"
@@ -55,9 +57,12 @@ class HistoryListGrid( grids.Grid ):
operations = [
grids.GridOperation( "Switch", allow_multiple=False, condition=( lambda item: not item.deleted ) ),
grids.GridOperation( "Share", condition=( lambda item: not item.deleted ) ),
grids.GridOperation( "Unshare", condition=( lambda item: not item.deleted ) ),
grids.GridOperation( "Rename", condition=( lambda item: not item.deleted ) ),
grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ) ),
grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) )
grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ),
grids.GridOperation( "Enable import via link", condition=( lambda item: item.deleted ) ),
grids.GridOperation( "Disable import via link", condition=( lambda item: item.deleted ) )
]
standard_filters = [
grids.GridColumnFilter( "Active", args=dict( deleted=False ) ),
@@ -99,7 +104,9 @@ class SharedHistoryListGrid( grids.Grid ):
]
operations = [
grids.GridOperation( "Clone" ),
grids.GridOperation( "Unshare" )
grids.GridOperation( "Unshare" ),
grids.GridOperation( "Enable import via link", condition=( lambda item: item.deleted ) ),
grids.GridOperation( "Disable import via link", condition=( lambda item: item.deleted ) )
]
standard_filters = []
def build_initial_query( self, session ):
@@ -126,19 +133,19 @@ class HistoryController( BaseController ):
current_history = trans.get_history()
status = message = None
if 'operation' in kwargs:
history_ids = util.listify( kwargs.get( 'id', [] ) )
histories = []
operation = kwargs['operation'].lower()
if operation == "share":
return self.share( trans, **kwargs )
elif operation == "rename":
if operation == "rename":
return self.rename( trans, **kwargs )
elif operation == 'sharing':
return self.sharing( trans, id=kwargs['id'] )
history_ids = util.listify( kwargs.get( 'id', [] ) )
if operation == "sharing":
return self.sharing( trans, id=history_ids )
# Display no message by default
status, message = None, None
refresh_history = False
# Load the histories and ensure they all belong to the current user
histories = []
for history_id in history_ids:
history = get_history( trans, history_id )
if history:
@@ -161,6 +168,21 @@ class HistoryController( BaseController ):
trans.template_context['refresh_frames'] = ['history']
elif operation == "undelete":
status, message = self._list_undelete( trans, histories )
elif operation == "unshare":
for history in histories:
husas = trans.app.model.HistoryUserShareAssociation.filter_by( history=history ).all()
for husa in husas:
husa.delete()
elif operation == "enable import via link":
for history in histories:
if not history.importable:
history.importable = True
elif operation == "disable import via link":
if history_ids:
histories = [ get_history( trans, history_id ) for history_id in history_ids ]
for history in histories:
if history.importable:
history.importable = False
trans.sa_session.flush()
# Render the list view
return self.stored_list_grid( trans, status=status, message=message, **kwargs )
@@ -237,24 +259,20 @@ class HistoryController( BaseController ):
msg = util.restore_text( kwargs.get( 'msg', '' ) )
status = message = None
if 'operation' in kwargs:
id = kwargs.get( 'id', None )
ids = util.listify( kwargs.get( 'id', [] ) )
operation = kwargs['operation'].lower()
if operation == "clone":
if not id:
if not ids:
message = "Select a history to clone"
return self.shared_list_grid( trans, status='error', message=message, **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:
if not ids:
message = "Select a history to unshare"
return self.shared_list_grid( trans, status='error', message=message, **kwargs )
ids = util.listify( id )
histories = []
for history_id in ids:
history = get_history( trans, history_id, check_ownership=False )
histories.append( history )
histories = [ get_history( trans, history_id ) for history_id in ids ]
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()
@@ -262,6 +280,20 @@ class HistoryController( BaseController ):
association.flush()
message = "Unshared %d shared histories" % len( ids )
status = 'done'
elif operation == "enable import via link":
if ids:
histories = [ get_history( trans, id ) for id in ids ]
for history in histories:
if not history.importable:
history.importable = True
history.flush()
elif operation == "disable import via link":
if ids:
histories = [ get_history( trans, id ) for id in ids ]
for history in histories:
if history.importable:
history.importable = False
history.flush()
# Render the list view
return self.shared_list_grid( trans, status=status, message=message, **kwargs )
@web.expose
@@ -622,7 +654,9 @@ class HistoryController( BaseController ):
params = util.Params( kwd )
msg = util.restore_text ( params.get( 'msg', '' ) )
if id:
histories = [ get_history( trans, id ) ]
ids = util.listify( id )
if ids:
histories = [ get_history( trans, history_id ) for history_id in ids ]
for history in histories:
if params.get( 'enable_import_via_link', False ):
history.importable = True
@@ -635,14 +669,34 @@ class HistoryController( BaseController ):
if not user:
msg = 'History (%s) does not seem to be shared with user (%s)' % ( history.name, user.email )
return trans.fill_template( 'history/sharing.mako', histories=histories, msg=msg, messagetype='error' )
association = trans.app.model.HistoryUserShareAssociation.filter_by( user=user, history=history ).one()
association.delete()
association.flush()
if not id:
shared_msg = "History (%s) now shared with: %d users. " % ( history.name, len( history.users_shared_with ) )
msg = '%s%s' % ( shared_msg, msg )
husas = trans.app.model.HistoryUserShareAssociation.filter_by( user=user, history=history ).all()
if husas:
for husa in husas:
husa.delete()
husa.flush()
histories = []
# Get all histories that have been shared with others
husas = trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
.join( "history" ) \
.filter( and_( trans.app.model.History.user == trans.user,
trans.app.model.History.deleted == False ) ) \
.order_by( trans.app.model.History.table.c.name ) \
.all()
for husa in husas:
history = husa.history
if history not in histories:
histories.append( history )
# Get all histories that are importable
importables = trans.sa_session.query( trans.app.model.History ) \
.filter_by( user=trans.user, importable=True, deleted=False ) \
.order_by( trans.app.model.History.table.c.name ) \
.all()
for importable in importables:
if importable not in histories:
histories.append( importable )
# Sort the list of histories by history.name
histories.sort( key=operator.attrgetter( 'name') )
return trans.fill_template( 'history/sharing.mako', histories=histories, msg=msg, messagetype='done' )
@web.expose
@web.require_login( "rename histories" )
def rename( self, trans, id=None, name=None, **kwd ):
+54 -66
View File
@@ -1,75 +1,63 @@
<%inherit file="/base.mako"/>
<%namespace file="/message.mako" import="render_msg" />
<h2>Public access via link</h2>
##<h2>Import via link</h2>
%if msg:
${render_msg( msg, messagetype )}
%endif
%for history in histories:
<p>
%if history.importable:
Send the following URL to users as an easy way for them to import the history, making a copy of their own:
<% url = h.url_for( controller='history', action='imp', id=trans.security.encode_id(history.id), qualified=True ) %>
<blockquote>
<a href="${url}">${url}</a>
</blockquote>
<br/>
<form action="${h.url_for( controller='history', action='sharing', id=trans.security.encode_id( history.id ) )}" method="POST">
<input class="action-button" type="submit" name="disable_import_via_link" value="Disable import via link">
</form>
%else:
This history is currently restricted (only you and the users listed below
can access it). Enabling the following option will generate a URL that you
can give to a user to allow them to import this history.
<br/>
<form action="${h.url_for( action='sharing', id=trans.security.encode_id(history.id) )}" method="POST">
<input class="action-button" type="submit" name="enable_import_via_link" value="Enable import via link">
</form>
%endif
</p>
<h2>Sharing with specific users</h2>
%if history.users_shared_with:
<ul class="manage-table-actions">
<li>
<a class="action-button" href="${h.url_for( controller='history', action='share', id=trans.security.encode_id( history.id ) )}">
<span>Share with another user</span>
</a>
</li>
</ul>
<p>
The following users will see this history in their list of histories
shared with them by others, and they will be able to create their own copy of it:
</p>
<table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr class="header">
<th>History '${history.name}' currently shared with</th>
<th></th>
</tr>
%for i, association in enumerate( history.users_shared_with ):
<% user = association.user %>
<tr>
<td>
${user.email}
<a id="user-${i}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
</td>
<td>
%if len( histories ) == 1:
## Only allow unsharing if we're dealing with 1 history, otherwise
## page refreshes screw things up
<div popupmenu="user-${i}-popup">
<a class="action-button" href="${h.url_for( controller='history', action='sharing', id=trans.security.encode_id( history.id ), unshare_user=trans.security.encode_id( user.id ) )}">Unshare</a>
<h2>Histories that you've shared with others or enabled to be imported</h2>
%if not histories:
You have no histories that you've shared with others or enabled to be imported
%else:
%for history in histories:
<div class="toolForm">
<div class="toolFormTitle">History '${history.name}' shared with</div>
<div class="toolFormBody">
<div class="form-row">
<div style="float: right;">
<a class="action-button" href="${h.url_for( controller='history', action='share', id=trans.security.encode_id( history.id ) )}">
<span>Share with another user</span>
</a>
</div>
</div>
%if history.users_shared_with:
%for i, association in enumerate( history.users_shared_with ):
<% user = association.user %>
<div class="form-row">
<a class="action-button" href="${h.url_for( controller='history', action='sharing', id=trans.security.encode_id( history.id ), unshare_user=trans.security.encode_id( user.id ) )}">Unshare</a>
${user.email}
</div>
%endfor
%endif
%if history.importable:
<div class="form-row">
<% url = h.url_for( controller='history', action='imp', id=trans.security.encode_id(history.id), qualified=True ) %>
<a href="${url}">${url}</a>
<div class="toolParamHelp" style="clear: both;">
Send the above URL to users as an easy way for them to import the history, making a copy of their own
</div>
</div>
<div class="form-row">
<form action="${h.url_for( controller='history', action='sharing', id=trans.security.encode_id( history.id ) )}" method="POST">
<div class="form-row">
<input class="action-button" type="submit" name="disable_import_via_link" value="Disable import via link">
</div>
%endif
</td>
</tr>
%endfor
</table>
%else:
<p>You have not shared this history with any users.</p>
<a class="action-button" href="${h.url_for( controller='history', action='share', id=trans.security.encode_id(history.id) )}">
<span>Share with another user</span>
</a>
%endif
%endfor
</form>
</div>
%else:
<form action="${h.url_for( action='sharing', id=trans.security.encode_id(history.id) )}" method="POST">
<div class="form-row">
<input class="action-button" type="submit" name="enable_import_via_link" value="Enable import via link">
<div class="toolParamHelp" style="clear: both;">
Click to generate a URL that you can give to a user to allow them to import this history, making a copy of their own
</div>
</div>
</form>
%endif
</div>
</div>
%endfor
%endif
+11 -5
View File
@@ -10,9 +10,6 @@
"Stored by you": function() {
galaxy_main.location = "${h.url_for( controller='history', action='list')}";
},
"Shared with you": function() {
galaxy_main.location = "${h.url_for( controller='history', action='list_shared')}";
},
"Current History": null,
"Create new": function() {
galaxy_history.location = "${h.url_for( controller='root', action='history_new' )}";
@@ -32,10 +29,19 @@
"Show deleted datasets": function() {
galaxy_history.location = "${h.url_for( controller='root', action='history', show_deleted=True)}";
},
"Delete": function() {
if ( confirm( "Really delete the current history?" ) ) {
"Delete": function()
{
if ( confirm( "Really delete the current history?" ) )
{
galaxy_main.location = "${h.url_for( controller='history', action='delete_current' )}";
}
},
"Manage shared histories": null,
"Shared by you": function() {
galaxy_main.location = "${h.url_for( controller='history', action='list', operation='sharing' )}";
},
"Shared with you": function() {
galaxy_main.location = "${h.url_for( controller='history', action='list_shared')}";
}
});
});
+8 -17
View File
@@ -141,14 +141,13 @@ class TestHistory( TwillTestCase ):
check_str_after_submit='You cannot send histories to yourself.' )
# Share history3 with 1 valid user
self.share_current_history( regular_user1.email,
check_str=history3.name,
check_str_after_submit='History (%s) now shared with: 1 users' % history3.name )
check_str=history3.name )
# Check out list of histories to make sure history3 was shared
self.view_stored_active_histories( check_str='operation=sharing&id=%s">shared' % self.security.encode_id( history3.id ) )
self.view_stored_active_histories( check_str='operation=sharing">shared' )
# Enable importing history3 via a URL
self.enable_import_via_link( self.security.encode_id( history3.id ),
check_str='Unshare',
check_str_after_submit='Send the following URL to users' )
check_str_after_submit='Send the above URL to users' )
# Make sure history3 is now import-able
history3.refresh()
if not history3.importable:
@@ -159,7 +158,7 @@ class TestHistory( TwillTestCase ):
check_str_after_submit='You cannot import your own history.' )
# Disable the import link for history3
self.disable_import_via_link( self.security.encode_id( history3.id ),
check_str='Send the following URL to users',
check_str='Send the above URL to users',
check_str_after_submit='Enable import via link' )
# Try importing history3 after disabling the URL
self.import_history_via_url( self.security.encode_id( history3.id ),
@@ -274,12 +273,10 @@ class TestHistory( TwillTestCase ):
self.upload_file( '2.bed', dbkey='hg18' )
ids = '%s,%s' % ( self.security.encode_id( history3.id ), self.security.encode_id( history4.id ) )
emails = '%s,%s' % ( regular_user2.email, regular_user3.email )
check_str_after_submit = 'History (%s) now shared with: 3 users.' % history3.name
self.share_histories_with_users( ids,
emails,
check_str1='Share 2 histories',
check_str2=history4.name,
check_str_after_submit=check_str_after_submit )
check_str2=history4.name )
self.logout()
self.login( email=regular_user2.email )
# Shared history3 should be in regular_user2's list of shared histories
@@ -342,12 +339,10 @@ class TestHistory( TwillTestCase ):
"""Testing sharing a restricted history by making the datasets public"""
# Logged in as admin_user
action_check_str = 'The following datasets can be shared with %s by updating their permissions' % regular_user1.email
action_check_str_after_submit = 'History (%s) now shared with: 1 users.' % history5.name
# Current history is history5
self.share_current_history( regular_user1.email,
action='public',
action_check_str=action_check_str,
action_check_str_after_submit=action_check_str_after_submit )
action_check_str=action_check_str )
self.logout()
self.login( email=regular_user1.email )
# Shared history5 should be in regular_user1's list of shared histories
@@ -375,12 +370,10 @@ class TestHistory( TwillTestCase ):
self.upload_file( '2.bed', dbkey='hg18' )
check_str_after_submit = 'The following datasets can be shared with %s with no changes' % regular_user2.email
check_str_after_submit2 = 'The following datasets can be shared with %s by updating their permissions' % regular_user2.email
action_check_str_after_submit = 'History (%s) now shared with: 2 users.' % history5.name
self.share_current_history( regular_user2.email,
check_str_after_submit=check_str_after_submit,
check_str_after_submit2=check_str_after_submit2,
action='private',
action_check_str_after_submit=action_check_str_after_submit )
action='private' )
# We should now have a new sharing role
global sharing_role
role_name = 'Sharing role for: %s, %s' % ( admin_user.email, regular_user2.email )
@@ -470,12 +463,10 @@ class TestHistory( TwillTestCase ):
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
# history5 will be shared with regular_user1, regular_user2 and regular_user3
action_check_str_after_submit = 'History (%s) now shared with: 3 users.' % history5.name
self.share_current_history( email,
check_str_after_submit=check_str_after_submit,
check_str_after_submit2=check_str_after_submit2,
action='share_anyway',
action_check_str_after_submit=action_check_str_after_submit )
action='share_anyway' )
# Check security on clone of history5 for regular_user2
self.logout()
self.login( email=regular_user2.email )