details: http://www.bx.psu.edu/hg/galaxy/rev/86dde83cab76 changeset: 2701:86dde83cab76 user: Greg Von Kuster <greg@bx.psu.edu> date: Wed Sep 16 14:06:14 2009 -0400 description: Use a grid for admin user management, still needs some polishing, but a good start. 20 file(s) affected in this change: lib/galaxy/web/controllers/admin.py lib/galaxy/web/controllers/history.py lib/galaxy/web/controllers/page.py lib/galaxy/web/controllers/requests.py lib/galaxy/web/controllers/requests_admin.py lib/galaxy/web/framework/helpers/grids.py templates/admin/dataset_security/deleted_groups.mako templates/admin/dataset_security/deleted_roles.mako templates/admin/dataset_security/groups.mako templates/admin/dataset_security/roles.mako templates/admin/dataset_security/user.mako templates/admin/dataset_security/users.mako templates/admin/user/create.mako templates/admin/user/deleted_users.mako templates/admin/user/grid.mako templates/admin/user/reset_password.mako templates/admin/user/user.mako templates/history/grid.mako test/base/twilltestcase.py test/functional/test_security_and_libraries.py diffs (1508 lines): diff -r c40962280e72 -r 86dde83cab76 lib/galaxy/web/controllers/admin.py --- a/lib/galaxy/web/controllers/admin.py Wed Sep 16 12:39:57 2009 -0400 +++ b/lib/galaxy/web/controllers/admin.py Wed Sep 16 14:06:14 2009 -0400 @@ -4,10 +4,88 @@ from galaxy.web.base.controller import * from galaxy.model.orm import * from galaxy.web.controllers.forms import get_all_forms, get_form_widgets +from galaxy.web.framework.helpers import time_ago, iff, grids import logging log = logging.getLogger( __name__ ) +class UserListGrid( grids.Grid ): + class EmailColumn( grids.GridColumn ): + def get_value( self, trans, grid, user ): + return user.email + class UserNameColumn( grids.GridColumn ): + def get_value( self, trans, grid, user ): + if user.username: + return user.username + return 'not set' + class GroupsColumn( grids.GridColumn ): + def get_value( self, trans, grid, user ): + if user.groups: + return len( user.groups ) + return 0 + class RolesColumn( grids.GridColumn ): + def get_value( self, trans, grid, user ): + if user.roles: + return len( user.roles ) + return 0 + class ExternalColumn( grids.GridColumn ): + def get_value( self, trans, grid, user ): + if user.external: + return 'yes' + return 'no' + class LastLoginColumn( grids.GridColumn ): + def get_value( self, trans, grid, user ): + if user.galaxy_sessions: + return self.format( user.galaxy_sessions[ 0 ].update_time ) + return 'never' + # Grid definition + title = "Users" + model_class = model.User + template='/admin/user/grid.mako' + columns = [ + EmailColumn( "Email", link=( lambda item: dict( operation="user", id=item.id ) ), attach_popup=True ), + UserNameColumn( "User Name", attach_popup=False ), + GroupsColumn( "Groups", attach_popup=False ), + RolesColumn( "Roles", attach_popup=False ), + ExternalColumn( "External", attach_popup=False ), + LastLoginColumn( "Last Login", format=time_ago ), + # Valid for filtering but invisible + grids.GridColumn( "Deleted", key="deleted", visible=False ) + ] + operations = [ + grids.GridOperation( "reset_password", condition=( lambda item: not item.deleted ), allow_multiple=True ) + ] + #TODO: enhance to account for trans.app.config.allow_user_deletion here so that we can eliminate these operations if + # the setting is False + operations.append( grids.GridOperation( "delete", condition=( lambda item: not item.deleted ), allow_multiple=True ) ) + operations.append( grids.GridOperation( "undelete", condition=( lambda item: item.deleted ), allow_multiple=True ) ) + operations.append( grids.GridOperation( "purge", condition=( lambda item: item.deleted ), allow_multiple=True ) ) + standard_filters = [ + grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), + grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), + grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + ] + default_filter = dict( deleted=False ) + def get_current_item( self, trans ): + return trans.user + def build_initial_query( self, session ): + return session.query( self.model_class ) + def apply_default_filter( self, trans, query, **kwargs ): + email_filter = kwargs.get( "email_filter", None ) + if email_filter: + if email_filter == 'all': + return query + else: + return query.filter( or_( trans.app.model.User.table.c.email.like( '%s' % email_filter.lower() + '%' ), + trans.app.model.User.table.c.email.like( '%s' % email_filter.upper() + '%' ) ) ) + elif query.count() > 200: + return query.filter( or_( trans.app.model.User.table.c.email.like( 'A%' ), + trans.app.model.User.table.c.email.like( 'a%' ) ) ) + return query + class Admin( BaseController ): + + user_list_grid = UserListGrid() + @web.expose @web.require_admin def index( self, trans, **kwd ): @@ -438,34 +516,41 @@ # Galaxy User Stuff @web.expose @web.require_admin - def create_new_user( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) + def create_new_user( self, trans, **kwargs ): email = '' password = '' confirm = '' subscribe = False - messagetype = params.get( 'messagetype', 'done' ) - if 'user_create_button' in kwd: - if 'email' in kwd: - email = kwd[ 'email' ] - if 'password' in kwd: - password = kwd[ 'password' ] - if 'confirm' in kwd: - confirm = kwd[ 'confirm' ] - if 'subscribe' in kwd: - subscribe = kwd[ 'subscribe' ] - messagetype = 'error' - if len( email ) == 0 or "@" not in email or "." not in email: - msg = "Please enter a real email address" + email_filter = kwargs.get( 'email_filter', 'A' ) + if 'user_create_button' in kwargs: + message = '' + status = '' + email = kwargs.get( 'email' , None ) + password = kwargs.get( 'password', None ) + confirm = kwargs.get( 'confirm', None ) + subscribe = kwargs.get( 'subscribe', None ) + if not email: + message = 'Enter a valid email address' + elif not password: + message = 'Enter a valid password' + elif not confirm: + message = 'Confirm the password' + elif len( email ) == 0 or "@" not in email or "." not in email: + message = 'Enter a real email address' elif len( email) > 255: - msg = "Email address exceeds maximum allowable length" + message = 'Email address exceeds maximum allowable length' elif trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first(): - msg = "User with that email already exists" + message = 'A user with that email already exists' elif len( password ) < 6: - msg = "Please use a password of at least 6 characters" + message = 'Use a password of at least 6 characters' elif password != confirm: - msg = "Passwords do not match" + message = 'Passwords do not match' + if message: + trans.response.send_redirect( web.url_for( controller='admin', + action='users', + email_filter=email_filter, + message=util.sanitize_text( message ), + status='error' ) ) else: user = trans.app.model.User( email=email ) user.set_password_cleartext( password ) @@ -474,20 +559,22 @@ user.flush() trans.app.security_agent.create_private_user_role( user ) trans.app.security_agent.user_set_default_permissions( user, history=False, dataset=False ) - trans.log_event( "Admin created a new account for user %s" % email ) - msg = 'Created new user account' - messagetype = 'done' + message = 'Created new user account (%s)' % user.email + status = 'done' #subscribe user to email list if subscribe: mail = os.popen( "%s -t" % trans.app.config.sendmail_path, 'w' ) mail.write( "To: %s\nFrom: %s\nSubject: Join Mailing List\n\nJoin Mailing list." % ( trans.app.config.mailing_join_addr, email ) ) if mail.close(): - msg + ". However, subscribing to the mailing list has failed." - messagetype = 'error' - trans.response.send_redirect( web.url_for( action='users', msg=util.sanitize_text( msg ), messagetype=messagetype ) ) + message + ". However, subscribing to the mailing list has failed." + status = 'error' + trans.response.send_redirect( web.url_for( controller='admin', + action='users', + email_filter=email_filter, + message=util.sanitize_text( message ), + status=status ) ) return trans.fill_template( '/admin/user/create.mako', - msg=msg, - messagetype=messagetype, + email_filter=email_filter, email=email, password=password, confirm=confirm, @@ -495,56 +582,76 @@ @web.expose @web.require_admin def reset_user_password( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - user_id = int( params.user_id ) - user = trans.app.model.User.filter( trans.app.model.User.table.c.id==user_id ).first() - password = '' - confirm = '' - messagetype = params.get( 'messagetype', 'done' ) + id = kwd.get( 'id', None ) + if not id: + message = "No user ids received for resetting passwords" + trans.response.send_redirect( web.url_for( action='users', message=message, status='error' ) ) + ids = util.listify( id ) if 'reset_user_password_button' in kwd: - if 'password' in kwd: - password = kwd[ 'password' ] - if 'confirm' in kwd: - confirm = kwd[ 'confirm' ] - messagetype = 'error' - if len( password ) < 6: - msg = "Please use a password of at least 6 characters" - elif password != confirm: - msg = "Passwords do not match" - else: - user.set_password_cleartext( password ) - user.flush() - trans.log_event( "Admin reset password for user %s" % user.email ) - msg = 'Password reset' - messagetype = 'done' - trans.response.send_redirect( web.url_for( action='users', msg=util.sanitize_text( msg ), messagetype=messagetype ) ) + message = '' + status = '' + for user_id in ids: + user = get_user( trans, user_id ) + password = kwd.get( 'password', None ) + confirm = kwd.get( 'confirm' , None ) + if len( password ) < 6: + message = "Please use a password of at least 6 characters" + status = 'error' + break + elif password != confirm: + message = "Passwords do not match" + status = 'error' + break + else: + user.set_password_cleartext( password ) + user.flush() + if not message and not status: + message = "Passwords reset for %d users" % len( ids ) + status = 'done' + trans.response.send_redirect( web.url_for( action='users', + message=util.sanitize_text( message ), + status=status ) ) + users = [ get_user( trans, user_id ) for user_id in ids ] + if len( ids ) > 1: + id=','.join( id ) return trans.fill_template( '/admin/user/reset_password.mako', - msg=msg, - messagetype=messagetype, - user=user, - password=password, - confirm=confirm ) + id=id, + users=users, + password='', + confirm='' ) @web.expose @web.require_admin def mark_user_deleted( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - user = trans.app.model.User.get( int( params.user_id ) ) - user.deleted = True - user.flush() - msg = "User '%s' has been marked as deleted." % user.email - trans.response.send_redirect( web.url_for( action='users', msg=util.sanitize_text( msg ), messagetype='done' ) ) + id = kwd.get( 'id', None ) + if not id: + message = "No user ids received for deleting" + trans.response.send_redirect( web.url_for( action='users', message=message, status='error' ) ) + ids = util.listify( id ) + for user_id in ids: + user = get_user( trans, user_id ) + user.deleted = True + user.flush() + message = "Deleted %d users" % len( ids ) + trans.response.send_redirect( web.url_for( action='users', message=util.sanitize_text( message ), status='done' ) ) @web.expose @web.require_admin def undelete_user( self, trans, **kwd ): - params = util.Params( kwd ) - user = trans.app.model.User.get( int( params.user_id ) ) - user.deleted = False - user.flush() - msg = "User '%s' has been marked as not deleted." % user.email - trans.response.send_redirect( web.url_for( action='users', msg=util.sanitize_text( msg ), messagetype='done' ) ) + id = kwd.get( 'id', None ) + if not id: + message = "No user ids received for undeleting" + trans.response.send_redirect( web.url_for( action='users', message=message, status='error' ) ) + ids = util.listify( id ) + count = 0 + for user_id in ids: + user = get_user( trans, user_id ) + if user.deleted: + user.deleted = False + user.flush() + count += 1 + message = "Undeleted %d users" % count + trans.response.send_redirect( web.url_for( action='users', + message=util.sanitize_text( message ), + status='done' ) ) @web.expose @web.require_admin def purge_user( self, trans, **kwd ): @@ -559,86 +666,111 @@ # - UserGroupAssociation where user_id == User.id # - UserRoleAssociation where user_id == User.id EXCEPT FOR THE PRIVATE ROLE # Purging Histories and Datasets must be handled via the cleanup_datasets.py script - params = util.Params( kwd ) - user = trans.app.model.User.get( int( params.user_id ) ) - if not user.deleted: - # We should never reach here, but just in case there is a bug somewhere... - msg = "User '%s' has not been deleted, so it cannot be purged." % user.email - trans.response.send_redirect( web.url_for( action='users', msg=util.sanitize_text( msg ), messagetype='error' ) ) - private_role = trans.app.security_agent.get_private_user_role( user ) - # Delete History - for h in user.active_histories: - h.refresh() - for hda in h.active_datasets: - # Delete HistoryDatasetAssociation - d = trans.app.model.Dataset.get( hda.dataset_id ) - # Delete Dataset - if not d.deleted: - d.deleted = True - d.flush() - hda.deleted = True - hda.flush() - h.deleted = True - h.flush() - # Delete UserGroupAssociations - for uga in user.groups: - uga.delete() - uga.flush() - # Delete UserRoleAssociations EXCEPT FOR THE PRIVATE ROLE - for ura in user.roles: - if ura.role_id != private_role.id: - ura.delete() - ura.flush() - # Purge the user - user.purged = True - user.flush() - msg = "User '%s' has been marked as purged." % user.email - trans.response.send_redirect( web.url_for( action='deleted_users', msg=util.sanitize_text( msg ), messagetype='done' ) ) + id = kwd.get( 'id', None ) + if not id: + message = "No user ids received for purging" + trans.response.send_redirect( web.url_for( action='users', + message=util.sanitize_text( message ), + status='error' ) ) + ids = util.listify( id ) + for user_id in ids: + user = get_user( trans, user_id ) + if not user.deleted: + # We should never reach here, but just in case there is a bug somewhere... + message = "User '%s' has not been deleted, so it cannot be purged." % user.email + trans.response.send_redirect( web.url_for( action='users', + message=util.sanitize_text( message ), + status='error' ) ) + private_role = trans.app.security_agent.get_private_user_role( user ) + # Delete History + for h in user.active_histories: + h.refresh() + for hda in h.active_datasets: + # Delete HistoryDatasetAssociation + d = trans.app.model.Dataset.get( hda.dataset_id ) + # Delete Dataset + if not d.deleted: + d.deleted = True + d.flush() + hda.deleted = True + hda.flush() + h.deleted = True + h.flush() + # Delete UserGroupAssociations + for uga in user.groups: + uga.delete() + uga.flush() + # Delete UserRoleAssociations EXCEPT FOR THE PRIVATE ROLE + for ura in user.roles: + if ura.role_id != private_role.id: + ura.delete() + ura.flush() + # Purge the user + user.purged = True + user.flush() + message = "Purged %d users" % len( ids ) + trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message=util.sanitize_text( message ), + status='done' ) ) @web.expose @web.require_admin - def deleted_users( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - users = trans.app.model.User.filter( and_( trans.app.model.User.table.c.deleted==True, trans.app.model.User.table.c.purged==False ) ) \ - .order_by( trans.app.model.User.table.c.email ) \ - .all() - return trans.fill_template( '/admin/user/deleted_users.mako', users=users, msg=msg, messagetype=messagetype ) - @web.expose - @web.require_admin - def users( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - users = trans.app.model.User.filter( trans.app.model.User.table.c.deleted==False ).order_by( trans.app.model.User.table.c.email ).all() - return trans.fill_template( '/admin/dataset_security/users.mako', - users=users, - allow_user_deletion=trans.app.config.allow_user_deletion, - msg=msg, - messagetype=messagetype ) + def users( self, trans, **kwargs ): + if 'operation' in kwargs: + operation = kwargs['operation'].lower() + if operation == "user": + return self.user( trans, **kwargs ) + if operation == "reset_password": + return self.reset_user_password( trans, **kwargs ) + if operation == "delete": + return self.mark_user_deleted( trans, **kwargs ) + if operation == "undelete": + return self.undelete_user( trans, **kwargs ) + if operation == "purge": + return self.purge_user( trans, **kwargs ) + if operation == "create": + return self.create_new_user( trans, **kwargs ) + # Render the list view + return self.user_list_grid( trans, **kwargs ) @web.expose @web.require_admin def user( self, trans, **kwd ): - params = util.Params( kwd ) - msg = util.restore_text( params.get( 'msg', '' ) ) - messagetype = params.get( 'messagetype', 'done' ) - user = trans.app.model.User.get( int( params.user_id ) ) + user_id = kwd.get( 'id', None ) + message = '' + status = '' + if not user_id: + message += "Invalid user id (%s) received" % str( user_id ) + trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message=util.sanitize_text( message ), + status='error' ) ) + user = get_user( trans, user_id ) private_role = trans.app.security_agent.get_private_user_role( user ) - if params.get( 'user_roles_groups_edit_button', False ): + if kwd.get( 'user_roles_groups_edit_button', False ): # Make sure the user is not dis-associating himself from his private role - out_roles = [ trans.app.model.Role.get( x ) for x in util.listify( params.out_roles ) ] + out_roles = kwd.get( 'out_roles', [] ) + if out_roles: + out_roles = [ trans.app.model.Role.get( x ) for x in util.listify( out_roles ) ] if private_role in out_roles: - msg += "You cannot eliminate a user's private role association. " - messagetype = 'error' - in_roles = [ trans.app.model.Role.get( x ) for x in util.listify( params.in_roles ) ] - out_groups = [ trans.app.model.Group.get( x ) for x in util.listify( params.out_groups ) ] - in_groups = [ trans.app.model.Group.get( x ) for x in util.listify( params.in_groups ) ] + message += "You cannot eliminate a user's private role association. " + status = 'error' + in_roles = kwd.get( 'in_roles', [] ) + if in_roles: + in_roles = [ trans.app.model.Role.get( x ) for x in util.listify( in_roles ) ] + out_groups = kwd.get( 'out_groups', [] ) + if out_groups: + out_groups = [ trans.app.model.Group.get( x ) for x in util.listify( out_groups ) ] + in_groups = kwd.get( 'in_groups', [] ) + if in_groups: + in_groups = [ trans.app.model.Group.get( x ) for x in util.listify( in_groups ) ] if in_roles: trans.app.security_agent.set_entity_user_associations( users=[ user ], roles=in_roles, groups=in_groups ) user.refresh() - msg += "User '%s' has been updated with %d associated roles and %d associated groups (private roles are not displayed)" % \ + message += "User '%s' has been updated with %d associated roles and %d associated groups (private roles are not displayed)" % \ ( user.email, len( in_roles ), len( in_groups ) ) - trans.response.send_redirect( web.url_for( action='users', msg=util.sanitize_text( msg ), messagetype=messagetype ) ) + trans.response.send_redirect( web.url_for( action='users', + message=util.sanitize_text( message ), + status='done' ) ) in_roles = [] out_roles = [] in_groups = [] @@ -653,20 +785,24 @@ # role, which should always be in in_roles. The check above is added as an additional # precaution, since for a period of time we were including private roles in the form fields. out_roles.append( ( role.id, role.name ) ) - for group in trans.app.model.Group.filter( trans.app.model.Group.table.c.deleted==False ).order_by( trans.app.model.Group.table.c.name ).all(): + for group in trans.app.model.Group.filter( trans.app.model.Group.table.c.deleted==False ) \ + .order_by( trans.app.model.Group.table.c.name ).all(): if group in [ x.group for x in user.groups ]: in_groups.append( ( group.id, group.name ) ) else: out_groups.append( ( group.id, group.name ) ) - msg += "User '%s' is currently associated with %d roles and is a member of %d groups" % ( user.email, len( in_roles ), len( in_groups ) ) - return trans.fill_template( '/admin/dataset_security/user.mako', + message += "User '%s' is currently associated with %d roles and is a member of %d groups" % \ + ( user.email, len( in_roles ), len( in_groups ) ) + if not status: + status = 'done' + return trans.fill_template( '/admin/user/user.mako', user=user, in_roles=in_roles, out_roles=out_roles, in_groups=in_groups, out_groups=out_groups, - msg=msg, - messagetype=messagetype ) + msg=message, + messagetype=status ) @web.expose @web.require_admin def memdump( self, trans, ids = 'None', sorts = 'None', pages = 'None', new_id = None, new_sort = None, **kwd ): @@ -749,3 +885,14 @@ else: last_updated[job.id] = '%s minutes' % int( delta.seconds / 60 ) return trans.fill_template( '/admin/jobs.mako', jobs = jobs, last_updated = last_updated, cutoff = cutoff, msg = msg, messagetype = messagetype ) + +## ---- Utility methods ------------------------------------------------------- + +def get_user( trans, id ): + """Get a User from the database by id.""" + # Load user from database + id = trans.security.decode_id( id ) + user = trans.sa_session.query( model.User ).get( id ) + if not user: + err+msg( "User not found" ) + return user diff -r c40962280e72 -r 86dde83cab76 lib/galaxy/web/controllers/history.py --- a/lib/galaxy/web/controllers/history.py Wed Sep 16 12:39:57 2009 -0400 +++ b/lib/galaxy/web/controllers/history.py Wed Sep 16 14:06:14 2009 -0400 @@ -84,7 +84,7 @@ default_filter = dict( deleted=False ) def get_current_item( self, trans ): return trans.get_history() - def apply_default_filter( self, trans, query ): + def apply_default_filter( self, trans, query, **kwargs ): return query.filter_by( user=trans.user, purged=False ) class SharedHistoryListGrid( grids.Grid ): @@ -121,7 +121,7 @@ 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 ): + def apply_default_filter( self, trans, query, **kwargs ): return query.filter( model.HistoryUserShareAssociation.user == trans.user ) class HistoryController( BaseController ): diff -r c40962280e72 -r 86dde83cab76 lib/galaxy/web/controllers/page.py --- a/lib/galaxy/web/controllers/page.py Wed Sep 16 12:39:57 2009 -0400 +++ b/lib/galaxy/web/controllers/page.py Wed Sep 16 14:06:14 2009 -0400 @@ -34,7 +34,7 @@ grids.GridOperation( "View", allow_multiple=False, url_args=dict( action='display') ), grids.GridOperation( "Edit", allow_multiple=False, url_args=dict( action='edit') ) ] - def apply_default_filter( self, trans, query ): + def apply_default_filter( self, trans, query, **kwargs ): return query.filter_by( user=trans.user ) class PageController( BaseController ): diff -r c40962280e72 -r 86dde83cab76 lib/galaxy/web/controllers/requests.py --- a/lib/galaxy/web/controllers/requests.py Wed Sep 16 12:39:57 2009 -0400 +++ b/lib/galaxy/web/controllers/requests.py Wed Sep 16 14:06:14 2009 -0400 @@ -51,14 +51,12 @@ return None def get_request_type(self, trans, request): return request.type.name - def apply_default_filter( self, trans, query ): + def apply_default_filter( self, trans, query, **kwargs ): return query.filter_by( user=trans.user ) def number_of_samples(self, trans, request): return str(len(request.samples)) def get_state(self, trans, request): return request.state - - class Requests( BaseController ): request_grid = RequestsListGrid() @@ -475,10 +473,6 @@ # The folders that should not be displayed may not be a complete list, but it is ultimately passed # to the calling method to keep from re-checking the same folders when the library / folder # select lists are rendered. - # - # TODO: RC, when you add the folders select list to your request form, take advantage of the hidden_folder_ids - # so that you do not need to check those same folders yet again when populating the select list. - # libraries = odict() for library in all_libraries: can_show, hidden_folder_ids = trans.app.security_agent.show_library_item( user, roles, library, actions_to_check ) @@ -721,10 +715,6 @@ # The folders that should not be displayed may not be a complete list, but it is ultimately passed # to the calling method to keep from re-checking the same folders when the library / folder # select lists are rendered. - # - # TODO: RC, when you add the folders select list to your request form, take advantage of the hidden_folder_ids - # so that you do not need to check those same folders yet again when populating the select list. - # libraries = {} for library in all_libraries: can_show, hidden_folder_ids = trans.app.security_agent.show_library_item( user, roles, library, actions_to_check ) diff -r c40962280e72 -r 86dde83cab76 lib/galaxy/web/controllers/requests_admin.py --- a/lib/galaxy/web/controllers/requests_admin.py Wed Sep 16 12:39:57 2009 -0400 +++ b/lib/galaxy/web/controllers/requests_admin.py Wed Sep 16 14:06:14 2009 -0400 @@ -53,9 +53,6 @@ def get_request_type(self, trans, request): request_type = trans.app.model.RequestType.get(request.request_type_id) return request_type.name -# def apply_default_filter( self, trans, query ): -# return query.filter(or_(self.model_class.state==self.model_class.states.SUBMITTED, -# self.model_class.state==self.model_class.states.COMPLETE)) def number_of_samples(self, trans, request): return str(len(request.samples)) diff -r c40962280e72 -r 86dde83cab76 lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py Wed Sep 16 12:39:57 2009 -0400 +++ b/lib/galaxy/web/framework/helpers/grids.py Wed Sep 16 14:06:14 2009 -0400 @@ -35,7 +35,7 @@ session = trans.sa_session # Build initial query query = self.build_initial_query( session ) - query = self.apply_default_filter( trans, query ) + query = self.apply_default_filter( trans, query, **kwargs ) # Maintain sort state in generated urls extra_url_args = {} # Process filtering arguments diff -r c40962280e72 -r 86dde83cab76 templates/admin/dataset_security/deleted_groups.mako --- a/templates/admin/dataset_security/deleted_groups.mako Wed Sep 16 12:39:57 2009 -0400 +++ b/templates/admin/dataset_security/deleted_groups.mako Wed Sep 16 14:06:14 2009 -0400 @@ -40,7 +40,7 @@ %else: <table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%"> <% - render_quick_find = len( groups ) > 50 + render_quick_find = len( groups ) > 200 ctr = 0 %> %if render_quick_find: diff -r c40962280e72 -r 86dde83cab76 templates/admin/dataset_security/deleted_roles.mako --- a/templates/admin/dataset_security/deleted_roles.mako Wed Sep 16 12:39:57 2009 -0400 +++ b/templates/admin/dataset_security/deleted_roles.mako Wed Sep 16 14:06:14 2009 -0400 @@ -42,7 +42,7 @@ %else: <table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%"> <% - render_quick_find = len( roles ) > 50 + render_quick_find = len( roles ) > 200 ctr = 0 %> %if render_quick_find: diff -r c40962280e72 -r 86dde83cab76 templates/admin/dataset_security/groups.mako --- a/templates/admin/dataset_security/groups.mako Wed Sep 16 12:39:57 2009 -0400 +++ b/templates/admin/dataset_security/groups.mako Wed Sep 16 14:06:14 2009 -0400 @@ -46,7 +46,7 @@ %else: <table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%"> <% - render_quick_find = len( groups ) > 50 + render_quick_find = len( groups ) > 200 ctr = 0 %> %if render_quick_find: diff -r c40962280e72 -r 86dde83cab76 templates/admin/dataset_security/roles.mako --- a/templates/admin/dataset_security/roles.mako Wed Sep 16 12:39:57 2009 -0400 +++ b/templates/admin/dataset_security/roles.mako Wed Sep 16 14:06:14 2009 -0400 @@ -48,7 +48,7 @@ %else: <table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%"> <% - render_quick_find = len( roles ) > 50 + render_quick_find = len( roles ) > 200 ctr = 0 %> %if render_quick_find: diff -r c40962280e72 -r 86dde83cab76 templates/admin/dataset_security/user.mako --- a/templates/admin/dataset_security/user.mako Wed Sep 16 12:39:57 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -<%inherit file="/base.mako"/> -<%namespace file="/message.mako" import="render_msg" /> - -<%def name="javascripts()"> - ${parent.javascripts()} - <script type="text/javascript"> - $(function(){ - $("input:text:first").focus(); - }) - </script> -</%def> - -<%def name="render_select( name, options )"> - <select name="${name}" id="${name}" style="min-width: 250px; height: 150px;" multiple> - %for option in options: - <option value="${option[0]}">${option[1]}</option> - %endfor - </select> -</%def> - -<script type="text/javascript"> -$().ready(function() { - $('#roles_add_button').click(function() { - return !$('#out_roles option:selected').remove().appendTo('#in_roles'); - }); - $('#roles_remove_button').click(function() { - return !$('#in_roles option:selected').remove().appendTo('#out_roles'); - }); - $('#groups_add_button').click(function() { - return !$('#out_groups option:selected').remove().appendTo('#in_groups'); - }); - $('#groups_remove_button').click(function() { - return !$('#in_groups option:selected').remove().appendTo('#out_groups'); - }); - $('form#associate_user_role_group').submit(function() { - $('#in_roles option').each(function(i) { - $(this).attr("selected", "selected"); - }); - $('#in_groups option').each(function(i) { - $(this).attr("selected", "selected"); - }); - }); -}); -</script> - -%if msg: - ${render_msg( msg, messagetype )} -%endif - -<div class="toolForm"> - <div class="toolFormTitle">User '${user.email}'</div> - <div class="toolFormBody"> - <form name="associate_user_role_group" id="associate_user_role_group" action="${h.url_for( action='user', user_id=user.id )}" method="post" > - <div class="form-row"> - <div style="float: left; margin-right: 10px;"> - <label>Roles associated with '${user.email}'</label> - ${render_select( "in_roles", in_roles )}<br/> - <input type="submit" id="roles_remove_button" value=">>"/> - </div> - <div> - <label>Roles not associated with '${user.email}'</label> - ${render_select( "out_roles", out_roles )}<br/> - <input type="submit" id="roles_add_button" value="<<"/> - </div> - </div> - <div class="form-row"> - <div style="float: left; margin-right: 10px;"> - <label>Groups associated with '${user.email}'</label> - ${render_select( "in_groups", in_groups )}<br/> - <input type="submit" id="groups_remove_button" value=">>"/> - </div> - <div> - <label>Groups not associated with '${user.email}'</label> - ${render_select( "out_groups", out_groups )}<br/> - <input type="submit" id="groups_add_button" value="<<"/> - </div> - </div> - <div class="form-row"> - <input type="submit" name="user_roles_groups_edit_button" value="Save"/> - </div> - </form> - </div> -</div> diff -r c40962280e72 -r 86dde83cab76 templates/admin/dataset_security/users.mako --- a/templates/admin/dataset_security/users.mako Wed Sep 16 12:39:57 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -<%inherit file="/base.mako"/> -<%namespace file="/message.mako" import="render_msg" /> - -## Render a row -<%def name="render_row( user, ctr, anchored, curr_anchor )"> - %if ctr % 2 == 1: - <tr class="odd_row"> - %else: - <tr> - %endif - <td> - <a href="${h.url_for( controller='admin', action='user', user_id=user.id )}">${user.email}</a> - <a id="user-${user.id}-popup" class="popup-arrow" style="display: none;">▼</a> - <div popupmenu="user-${user.id}-popup"> - <a class="action-button" href="${h.url_for( action='reset_user_password', user_id=user.id )}">Reset password</a> - <a class="action-button" href="${h.url_for( controller='admin', action='user', user_id=user.id )}">Change associated groups and roles</a> - %if allow_user_deletion: - <a class="action-button" href="${h.url_for( action='mark_user_deleted', user_id=user.id )}">Mark user deleted</a> - %endif - </div> - </td> - <td> - ${len( user.groups )} - </td> - <td> - ${len( user.roles )} - %if not anchored: - <a name="${curr_anchor}"></a> - <div style="float: right;"><a href="#TOP">top</a></div> - %endif - </td> - </tr> -</%def> - -<a name="TOP"><h2>Users</h2></a> - -<ul class="manage-table-actions"> - <li><a class="action-button" href="${h.url_for( controller='admin', action='create_new_user' )}">Create a new user</a></li> - %if allow_user_deletion: - <li><a class="action-button" href="${h.url_for( controller='admin', action='deleted_users' )}">Manage deleted users</a></li> - %endif -</ul> - -%if msg: - ${render_msg( msg, messagetype )} -%endif - -%if len( users ) == 0: - There are no Galaxy users -%else: - <table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%"> - <% - render_quick_find = len( users ) > 50 - ctr = 0 - %> - %if render_quick_find: - <% - anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'] - anchor_loc = 0 - anchored = False - curr_anchor = 'A' - %> - <tr style="background: #EEE"> - <td colspan="3" style="border-bottom: 1px solid #D8B365; text-align: center;"> - Jump to letter: - %for a in anchors: - | <a href="#${a}">${a}</a> - %endfor - </td> - </tr> - %endif - <tr class="header"> - <td>Email</td> - <td>Groups</td> - <td>Roles</td> - </tr> - %for ctr, user in enumerate( users ): - %if render_quick_find and not user.email.upper().startswith( curr_anchor ): - <% anchored = False %> - %endif - %if render_quick_find and user.email.upper().startswith( curr_anchor ): - %if not anchored: - ${render_row( user, ctr, anchored, curr_anchor )} - <% anchored = True %> - %else: - ${render_row( user, ctr, anchored, curr_anchor )} - %endif - %elif render_quick_find: - %for anchor in anchors[ anchor_loc: ]: - %if user.email.upper().startswith( anchor ): - %if not anchored: - <% curr_anchor = anchor %> - ${render_row( user, ctr, anchored, curr_anchor )} - <% anchored = True %> - %else: - ${render_row( user, ctr, anchored, curr_anchor )} - %endif - <% - anchor_loc = anchors.index( anchor ) - break - %> - %endif - %endfor - %else: - ${render_row( user, ctr, True, '' )} - %endif - %endfor - </table> -%endif diff -r c40962280e72 -r 86dde83cab76 templates/admin/user/create.mako --- a/templates/admin/user/create.mako Wed Sep 16 12:39:57 2009 -0400 +++ b/templates/admin/user/create.mako Wed Sep 16 14:06:14 2009 -0400 @@ -8,7 +8,7 @@ <div class="toolForm"> <div class="toolFormTitle">Create user account</div> <div class="toolFormBody"> - <form name="form" action="${h.url_for( controller='admin', action='create_new_user' )}" method="post" > + <form name="form" action="${h.url_for( controller='admin', action='create_new_user', email_filter=email_filter )}" method="post" > <div class="form-row"> <label>Email address:</label> <div style="float: left; width: 250px; margin-right: 10px;"> diff -r c40962280e72 -r 86dde83cab76 templates/admin/user/deleted_users.mako --- a/templates/admin/user/deleted_users.mako Wed Sep 16 12:39:57 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -<%inherit file="/base.mako"/> -<%namespace file="/message.mako" import="render_msg" /> - -## Render a row -<%def name="render_row( user, ctr, anchored, curr_anchor )"> - %if ctr % 2 == 1: - <tr class="odd_row"> - %else: - <tr> - %endif - <td> - ${user.email} - <a id="user-${user.id}-popup" class="popup-arrow" style="display: none;">▼</a> - <div popupmenu="user-${user.id}-popup"> - <a class="action-button" href="${h.url_for( controller="admin", action='undelete_user', user_id=user.id )}">Undelete</a> - <a class="action-button" confirm="Purging a user will delete all of their histories, datasets, and group and role associations. Click OK to purge the user '${user.email}'" href="${h.url_for( controller="admin", action='purge_user', user_id=user.id )}">Purge</a> - </div> - %if not anchored: - <a name="${curr_anchor}"></a> - <div style="float: right;"><a href="#TOP">top</a></div> - %endif - </td> - </tr> -</%def> - -<a name="TOP"><h2>Deleted Users</h2></a> - -%if msg: - ${render_msg( msg, messagetype )} -%endif - -%if len( users ) == 0: - There are no deleted Galaxy users -%else: - <table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%"> - <% - render_quick_find = len( users ) > 50 - ctr = 0 - %> - %if render_quick_find: - <% - anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'] - anchor_loc = 0 - anchored = False - curr_anchor = 'A' - %> - <tr style="background: #EEE"> - <td colspan="3" style="text-align: center; border-bottom: 1px solid #D8B365"> - Jump to letter: - %for a in anchors: - | <a href="#${a}">${a}</a> - %endfor - </td> - </tr> - %endif - <tr class="header"> - <td>Email</td> - </tr> - %for ctr, user in enumerate( users ): - %if render_quick_find and not user.email.upper().startswith( curr_anchor ): - <% anchored = False %> - %endif - %if render_quick_find and user.email.upper().startswith( curr_anchor ): - %if not anchored: - ${render_row( user, ctr, anchored, curr_anchor )} - <% anchored = True %> - %else: - ${render_row( user, ctr, anchored, curr_anchor )} - %endif - %elif render_quick_find: - %for anchor in anchors[ anchor_loc: ]: - %if user.email.upper().startswith( anchor ): - %if not anchored: - <% curr_anchor = anchor %> - ${render_row( user, ctr, anchored, curr_anchor )} - <% anchored = True %> - %else: - ${render_row( user, ctr, anchored, curr_anchor )} - %endif - <% - anchor_loc = anchors.index( anchor ) - break - %> - %endif - %endfor - %else: - ${render_row( user, ctr, True, '' )} - %endif - %endfor - </table> -%endif diff -r c40962280e72 -r 86dde83cab76 templates/admin/user/grid.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/admin/user/grid.mako Wed Sep 16 14:06:14 2009 -0400 @@ -0,0 +1,224 @@ +<%inherit file="/base.mako"/> +<%def name="title()">${grid.title}</%def> +<% from galaxy import util %> + +%if message: + <p> + <div class="${message_type}message transient-message">${util.restore_text( message )}</div> + <div style="clear: both"></div> + </p> +%endif + +<%def name="javascripts()"> + ${parent.javascripts()} + ${h.js("jquery.autocomplete", "autocomplete_tagging" )} + <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()"> + ${h.css( "base", "autocomplete_tagging" )} + <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 + +<ul class="manage-table-actions"> + <li><a class="action-button" href="${h.url_for( controller='admin', action='users', operation='create' )}">Create a new user</a></li> +</ul> + +%if query.count() == 0: + No users were returned for the current query. Click the Show all users button or a letter below. +%endif: +<% + letters = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', '0'] +%> +<div class="toolFormBody"> + <div class="form-row"> + <a class="action-button" href="${h.url_for( controller='admin', action='users', email_filter='all' )}"><span>Show all users</span></a> + <div class="toolParamHelp" style="clear: both;"> + Default behavior is displaying only users whose email begins with 'a' if there are more than 200 total users + </div> + </div> + <div class="form-row"> + <label>Show only users whose email starts with:</label> + %for letter in letters: + | <a href="${h.url_for( controller='admin', action='users', email_filter=letter )}">${letter}</a> + %endfor + <div style="clear: both"></div> + </div> + <form name="user_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="arrow" style="display: none;"><span>▼</span></a>' % i + else: + extra = "" + %> + %if href: + <td><div class="menubutton split" style="float: left;"><a class="label" href="${href}">${v}${extra}</a> </td> + %else: + <td >${v}${extra}</td> + %endif + %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> + %if grid.operations: + <tfoot> + <tr> + <td></td> + <td colspan="100"> + For <span class="grid-selected-count"></span> selected users: + %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> + %endif + </table> + </form> +</div> diff -r c40962280e72 -r 86dde83cab76 templates/admin/user/reset_password.mako --- a/templates/admin/user/reset_password.mako Wed Sep 16 12:39:57 2009 -0400 +++ b/templates/admin/user/reset_password.mako Wed Sep 16 14:06:14 2009 -0400 @@ -6,15 +6,17 @@ %endif <div class="toolForm"> - <div class="toolFormTitle">Reset user password</div> + <div class="toolFormTitle">Reset password for users</div> <div class="toolFormBody"> <form name="form" action="${h.url_for( controller='admin', action='reset_user_password' )}" method="post" > - <input type="hidden" name="user_id" value="${user.id}" size="40"> - <div class="form-row"> - <label>Email:</label> - ${user.email} - <div style="clear: both"></div> - </div> + <input type="hidden" name="id" value="${id}" size="40"> + %for user in users: + <div class="form-row"> + <label>Email:</label> + ${user.email} + <div style="clear: both"></div> + </div> + %endfor <div class="form-row"> <label>Password:</label> <div style="float: left; width: 250px; margin-right: 10px;"> diff -r c40962280e72 -r 86dde83cab76 templates/admin/user/user.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/admin/user/user.mako Wed Sep 16 14:06:14 2009 -0400 @@ -0,0 +1,83 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="javascripts()"> + ${parent.javascripts()} + <script type="text/javascript"> + $(function(){ + $("input:text:first").focus(); + }) + </script> +</%def> + +<%def name="render_select( name, options )"> + <select name="${name}" id="${name}" style="min-width: 250px; height: 150px;" multiple> + %for option in options: + <option value="${option[0]}">${option[1]}</option> + %endfor + </select> +</%def> + +<script type="text/javascript"> +$().ready(function() { + $('#roles_add_button').click(function() { + return !$('#out_roles option:selected').remove().appendTo('#in_roles'); + }); + $('#roles_remove_button').click(function() { + return !$('#in_roles option:selected').remove().appendTo('#out_roles'); + }); + $('#groups_add_button').click(function() { + return !$('#out_groups option:selected').remove().appendTo('#in_groups'); + }); + $('#groups_remove_button').click(function() { + return !$('#in_groups option:selected').remove().appendTo('#out_groups'); + }); + $('form#associate_user_role_group').submit(function() { + $('#in_roles option').each(function(i) { + $(this).attr("selected", "selected"); + }); + $('#in_groups option').each(function(i) { + $(this).attr("selected", "selected"); + }); + }); +}); +</script> + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +<div class="toolForm"> + <div class="toolFormTitle">User '${user.email}'</div> + <div class="toolFormBody"> + <form name="associate_user_role_group" id="associate_user_role_group" action="${h.url_for( action='user', id=trans.app.security.encode_id( user.id ) )}" method="post" > + <div class="form-row"> + <div style="float: left; margin-right: 10px;"> + <label>Roles associated with '${user.email}'</label> + ${render_select( "in_roles", in_roles )}<br/> + <input type="submit" id="roles_remove_button" value=">>"/> + </div> + <div> + <label>Roles not associated with '${user.email}'</label> + ${render_select( "out_roles", out_roles )}<br/> + <input type="submit" id="roles_add_button" value="<<"/> + </div> + </div> + <div class="form-row"> + <div style="float: left; margin-right: 10px;"> + <label>Groups associated with '${user.email}'</label> + ${render_select( "in_groups", in_groups )}<br/> + <input type="submit" id="groups_remove_button" value=">>"/> + </div> + <div> + <label>Groups not associated with '${user.email}'</label> + ${render_select( "out_groups", out_groups )}<br/> + <input type="submit" id="groups_add_button" value="<<"/> + </div> + </div> + <div class="form-row"> + <input type="submit" name="user_roles_groups_edit_button" value="Save"/> + </div> + </form> + </div> +</div> diff -r c40962280e72 -r 86dde83cab76 templates/history/grid.mako --- a/templates/history/grid.mako Wed Sep 16 12:39:57 2009 -0400 +++ b/templates/history/grid.mako Wed Sep 16 14:06:14 2009 -0400 @@ -179,18 +179,20 @@ </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> + %if grid.operations: + <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> + %endif </table> </form> diff -r c40962280e72 -r 86dde83cab76 test/base/twilltestcase.py --- a/test/base/twilltestcase.py Wed Sep 16 12:39:57 2009 -0400 +++ b/test/base/twilltestcase.py Wed Sep 16 14:06:14 2009 -0400 @@ -779,8 +779,8 @@ def create_new_account_as_admin( self, email='test4@bx.psu.edu', password='testuser' ): """Create a new account for another user""" self.home() - self.visit_url( "%s/admin/create_new_user?email=%s&password=%s&confirm=%s&user_create_button=%s" \ - % ( self.url, email, password, password, 'Create' ) ) + self.visit_url( "%s/admin/create_new_user?email=%s&password=%s&confirm=%s&user_create_button=Create&subscribe=False" \ + % ( self.url, email, password, password ) ) try: self.check_page_for_string( "Created new user account" ) previously_created = False @@ -790,34 +790,34 @@ previously_created = True self.home() return previously_created - def reset_password_as_admin( self, user_id=4, password='testreset' ): + def reset_password_as_admin( self, user_id, password='testreset' ): """Reset a user password""" self.home() - self.visit_url( "%s/admin/reset_user_password?user_id=%s" % ( self.url, str( user_id ) ) ) + self.visit_url( "%s/admin/reset_user_password?id=%s" % ( self.url, user_id ) ) tc.fv( "1", "password", password ) tc.fv( "1", "confirm", password ) tc.submit( "reset_user_password_button" ) - self.check_page_for_string( "Password reset" ) + self.check_page_for_string( "Passwords reset for 1 users" ) self.home() - def mark_user_deleted( self, user_id=4, email='' ): + def mark_user_deleted( self, user_id, email='' ): """Mark a user as deleted""" self.home() - self.visit_url( "%s/admin/mark_user_deleted?user_id=%s" % ( self.url, str( user_id ) ) ) - check_str = "User '%s' has been marked as deleted." % email + self.visit_url( "%s/admin/mark_user_deleted?id=%s" % ( self.url, user_id ) ) + check_str = "Deleted 1 users" self.check_page_for_string( check_str ) self.home() - def undelete_user( self, user_id=4, email='' ): + def undelete_user( self, user_id, email='' ): """Undelete a user""" self.home() - self.visit_url( "%s/admin/undelete_user?user_id=%s" % ( self.url, user_id ) ) - check_str = "User '%s' has been marked as not deleted" % email + self.visit_url( "%s/admin/undelete_user?id=%s" % ( self.url, user_id ) ) + check_str = "Undeleted 1 users" self.check_page_for_string( check_str ) self.home() def purge_user( self, user_id, email ): """Purge a user account""" self.home() - self.visit_url( "%s/admin/purge_user?user_id=%s" % ( self.url, user_id ) ) - check_str = "User '%s' has been marked as purged." % email + self.visit_url( "%s/admin/purge_user?id=%s" % ( self.url, user_id ) ) + check_str = "Purged 1 users" self.check_page_for_string( check_str ) self.home() def associate_roles_and_groups_with_user( self, user_id, email, @@ -825,7 +825,7 @@ in_group_ids=[], out_group_ids=[], check_str='' ): self.home() - url = "%s/admin/user?user_id=%s&user_roles_groups_edit_button=Save" % ( self.url, user_id ) + url = "%s/admin/user?id=%s&user_roles_groups_edit_button=Save" % ( self.url, user_id ) if in_role_ids: url += "&in_roles=%s" % ','.join( in_role_ids ) if out_role_ids: diff -r c40962280e72 -r 86dde83cab76 test/functional/test_security_and_libraries.py --- a/test/functional/test_security_and_libraries.py Wed Sep 16 12:39:57 2009 -0400 +++ b/test/functional/test_security_and_libraries.py Wed Sep 16 14:06:14 2009 -0400 @@ -70,11 +70,11 @@ raise AssertionError( 'The DefaultHistoryPermission.action for history id %d is "%s", but it should be "%s"' \ % ( latest_history.id, dhp.action, galaxy.model.Dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS.action ) ) self.home() - self.visit_url( "%s/admin/user?user_id=%s" % ( self.url, admin_user.id ) ) + self.visit_url( "%s/admin/user?id=%s" % ( self.url, self.security.encode_id( admin_user.id ) ) ) self.check_page_for_string( admin_user.email ) # Try deleting the admin_user's private role check_str = "You cannot eliminate a user's private role association." - self.associate_roles_and_groups_with_user( str( admin_user.id ), admin_user.email, + self.associate_roles_and_groups_with_user( self.security.encode_id( admin_user.id ), admin_user.email, out_role_ids=str( admin_user_private_role.id ), check_str=check_str ) self.logout() @@ -158,6 +158,7 @@ raise AssertionError( 'DatasetPermissionss "%s" for dataset id %d differ from DefaultHistoryPermissions "%s" for history id %d' \ % ( str( dps ), latest_dataset.id, str( dhps ), latest_history.id ) ) self.logout() + def test_015_login_as_regular_user2( self ): """Testing logging in as regular user test2@bx.psu.edu - tests changing DefaultHistoryPermissions for the current history""" self.login( email='test2@bx.psu.edu' ) # This will not be an admin user @@ -248,7 +249,7 @@ def test_025_reset_password_as_admin( self ): """Testing reseting a user password as admin""" email = 'test3@bx.psu.edu' - self.reset_password_as_admin( user_id=regular_user3.id, password='testreset' ) + self.reset_password_as_admin( user_id=self.security.encode_id( regular_user3.id ), password='testreset' ) self.logout() def test_030_login_after_password_reset( self ): """Testing logging in after an admin reset a password - tests DefaultHistoryPermissions for accounts created by an admin""" @@ -281,17 +282,17 @@ self.logout() # Reset the password to the default for later tests self.login( email='test@bx.psu.edu' ) - self.reset_password_as_admin( user_id=regular_user3.id, password='testuser' ) + self.reset_password_as_admin( user_id=self.security.encode_id( regular_user3.id ), password='testuser' ) def test_035_mark_user_deleted( self ): """Testing marking a user account as deleted""" - self.mark_user_deleted( user_id=regular_user3.id, email=regular_user3.email ) + self.mark_user_deleted( user_id=self.security.encode_id( regular_user3.id ), email=regular_user3.email ) # Deleting a user should not delete any associations regular_user3.refresh() if not regular_user3.active_histories: raise AssertionError( 'HistoryDatasetAssociations for regular_user3 were incorrectly deleted when the user was marked deleted' ) def test_040_undelete_user( self ): """Testing undeleting a user account""" - self.undelete_user( user_id=regular_user3.id, email=regular_user3.email ) + self.undelete_user( user_id=self.security.encode_id( regular_user3.id ), email=regular_user3.email ) def test_045_create_role( self ): """Testing creating new role with 3 members ( and a new group named the same ), then renaming the role""" name = 'Role One' @@ -436,7 +437,7 @@ for uga in admin_user.groups: group_ids.append( str( uga.group_id ) ) check_str = "User '%s' has been updated with %d associated roles and %d associated groups" % ( admin_user.email, len( role_ids ), len( group_ids ) ) - self.associate_roles_and_groups_with_user( str( admin_user.id ), str( admin_user.email ), + self.associate_roles_and_groups_with_user( self.security.encode_id( admin_user.id ), str( admin_user.email ), in_role_ids=role_ids, in_group_ids=group_ids, check_str=check_str ) admin_user.refresh() # admin_user should now be associated with 4 roles: private, role_one, role_two, role_three @@ -1610,9 +1611,9 @@ self.home() def test_250_purge_user( self ): """Testing purging a user account""" - self.mark_user_deleted( user_id=regular_user3.id, email=regular_user3.email ) + self.mark_user_deleted( user_id=self.security.encode_id( regular_user3.id ), email=regular_user3.email ) regular_user3.refresh() - self.purge_user( str( regular_user3.id ), regular_user3.email ) + self.purge_user( self.security.encode_id( regular_user3.id ), regular_user3.email ) regular_user3.refresh() if not regular_user3.purged: raise AssertionError( 'User %s was not marked as purged.' % regular_user3.email )