diff --git a/lib/galaxy/datatypes/data.py b/lib/galaxy/datatypes/data.py index 6a15e8731b8..a921b8c0b0e 100644 --- a/lib/galaxy/datatypes/data.py +++ b/lib/galaxy/datatypes/data.py @@ -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: diff --git a/lib/galaxy/web/controllers/history.py b/lib/galaxy/web/controllers/history.py index f436dde096b..0cb84c58012 100644 --- a/lib/galaxy/web/controllers/history.py +++ b/lib/galaxy/web/controllers/history.py @@ -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 ): diff --git a/templates/history/sharing.mako b/templates/history/sharing.mako index b705d5ac119..f525656a0bc 100644 --- a/templates/history/sharing.mako +++ b/templates/history/sharing.mako @@ -1,75 +1,63 @@ <%inherit file="/base.mako"/> <%namespace file="/message.mako" import="render_msg" /> -

Public access via link

+##

Import via link

%if msg: ${render_msg( msg, messagetype )} %endif -%for history in histories: -

- %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 ) %> -

- ${url} -
-
-
- -
- %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. -
-
- -
- %endif -

-

Sharing with specific users

- %if history.users_shared_with: - -

- 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: -

- - - - - - %for i, association in enumerate( history.users_shared_with ): - <% user = association.user %> - - - - - %endfor -
History '${history.name}' currently shared with
- ${user.email} - - - %if len( histories ) == 1: - ## Only allow unsharing if we're dealing with 1 history, otherwise - ## page refreshes screw things up -
- Unshare +

Histories that you've shared with others or enabled to be imported

+ +%if not histories: + You have no histories that you've shared with others or enabled to be imported +%else: + %for history in histories: +
+
History '${history.name}' shared with
+
+ + %if history.users_shared_with: + %for i, association in enumerate( history.users_shared_with ): + <% user = association.user %> +
+ Unshare + ${user.email} +
+ %endfor + %endif + %if history.importable: +
+ <% url = h.url_for( controller='history', action='imp', id=trans.security.encode_id(history.id), qualified=True ) %> + ${url} +
+ Send the above URL to users as an easy way for them to import the history, making a copy of their own +
+
+
+
+
+
- %endif -
- %else: -

You have not shared this history with any users.

- - Share with another user - - %endif -%endfor + + + %else: +
+
+ +
+ 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 +
+
+
+ %endif + + + %endfor +%endif diff --git a/templates/root/index.mako b/templates/root/index.mako index cf7c2922a8e..5689ea8af62 100644 --- a/templates/root/index.mako +++ b/templates/root/index.mako @@ -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')}"; } }); }); diff --git a/test/functional/test_history_functions.py b/test/functional/test_history_functions.py index e34e1e4d627..cdc2f233e62 100644 --- a/test/functional/test_history_functions.py +++ b/test/functional/test_history_functions.py @@ -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 )