From f0396dbc188aa381e9fa2d953900583e08e010ad Mon Sep 17 00:00:00 2001 From: Greg Von Kuster Date: Thu, 14 Aug 2008 16:18:06 -0400 Subject: [PATCH] First pass at administration components that incorporate dataset security and libraries. --- lib/galaxy/config.py | 2 +- lib/galaxy/model/__init__.py | 15 +- lib/galaxy/model/mapping.py | 12 +- lib/galaxy/security/__init__.py | 7 +- lib/galaxy/web/controllers/admin.py | 680 +++++++++++- lib/galaxy/web/controllers/library.py | 201 +--- .../admin/dataset_security/group_create.mako | 96 ++ .../group_dataset_permitted_actions_edit.mako | 71 ++ .../admin/dataset_security/group_members.mako | 47 + .../dataset_security/group_members_edit.mako | 114 ++ templates/admin/dataset_security/groups.mako | 67 ++ templates/admin/dataset_security/index.mako | 13 + .../specified_users_groups.mako | 56 + templates/admin/dataset_security/users.mako | 80 ++ templates/admin/index.mako | 13 + templates/admin/library/dataset.mako | 85 ++ templates/admin/library/folder.mako | 158 +++ templates/admin/library/libraries.mako | 19 + templates/admin/library/library.mako | 35 + templates/admin/library/new_dataset.mako | 953 +++++++++++++++++ templates/admin/reload_tool.mako | 28 + templates/admin_main.mako | 39 - templates/library/admin_list_libraries.mako | 16 - templates/library/libraries.mako | 13 + .../{user_view_library.mako => library.mako} | 52 +- templates/library/manage_dataset.mako | 100 -- templates/library/manage_folder.mako | 191 ---- templates/library/manage_library.mako | 46 - templates/library/new_dataset.mako | 997 ------------------ templates/library/user_list_libraries.mako | 14 - templates/root/masthead.mako | 3 + universe_wsgi.ini.sample | 4 +- 32 files changed, 2557 insertions(+), 1670 deletions(-) create mode 100644 templates/admin/dataset_security/group_create.mako create mode 100644 templates/admin/dataset_security/group_dataset_permitted_actions_edit.mako create mode 100644 templates/admin/dataset_security/group_members.mako create mode 100644 templates/admin/dataset_security/group_members_edit.mako create mode 100644 templates/admin/dataset_security/groups.mako create mode 100644 templates/admin/dataset_security/index.mako create mode 100644 templates/admin/dataset_security/specified_users_groups.mako create mode 100644 templates/admin/dataset_security/users.mako create mode 100644 templates/admin/index.mako create mode 100644 templates/admin/library/dataset.mako create mode 100644 templates/admin/library/folder.mako create mode 100644 templates/admin/library/libraries.mako create mode 100644 templates/admin/library/library.mako create mode 100644 templates/admin/library/new_dataset.mako create mode 100644 templates/admin/reload_tool.mako delete mode 100644 templates/admin_main.mako delete mode 100644 templates/library/admin_list_libraries.mako create mode 100644 templates/library/libraries.mako rename templates/library/{user_view_library.mako => library.mako} (58%) delete mode 100644 templates/library/manage_dataset.mako delete mode 100644 templates/library/manage_folder.mako delete mode 100644 templates/library/manage_library.mako delete mode 100644 templates/library/new_dataset.mako delete mode 100644 templates/library/user_list_libraries.mako diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py index 432499bef7a..ac54a889b7a 100644 --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -45,7 +45,7 @@ class Configuration( object ): self.job_scheduler_policy = kwargs.get("job_scheduler_policy", "FIFO") self.job_queue_cleanup_interval = int( kwargs.get("job_queue_cleanup_interval", "5") ) self.job_working_directory = resolve_path( kwargs.get( "job_working_directory", "database/job_working_directory" ), self.root ) - self.admin_pass = kwargs.get('admin_pass',"galaxy") + self.admin_users = kwargs.get( "admin_users", "" ) self.sendmail_path = kwargs.get('sendmail_path',"/usr/sbin/sendmail") self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-user-join@bx.psu.edu") self.error_email_to = kwargs.get( 'error_email_to', None ) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index e6637b2ad7f..460b6433b1d 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -129,20 +129,18 @@ class Group( object ): self.priority = priority @classmethod def get_public_group( cls ): - # TODO, Nate: Make sure this method is functionally correct. return Group.get( cls.public_id ) @classmethod def set_public_group( cls, group ): - # TODO, Nate: Make sure this method is functionally correct. - #we store the id instead of the object, because of alchemy sessions + # We store the id instead of the object, because of alchemy sessions if isinstance( group, Group ): group = group.id cls.public_id = group @classmethod def guess_public_group( cls ): - # TODO, Nate: Make sure this method is functionally correct. - #retrieve from database and store public group id, assume first created group is public - cls.set_public_group( Group.select( order_by = Group.table.c.create_time )[0] ) + # Retrieve from database and store public group id + group = Group.select_by( name='public' )[0] + cls.set_public_group( group ) class UserGroupAssociation( object ): def __init__( self, user, group ): @@ -262,7 +260,6 @@ class Dataset( object ): except OSError, e: log.critical('%s delete error %s' % (self.__class__.__name__, e)) - class DatasetInstance( object ): """A base class for all 'dataset instances', HDAs, LDAs, etc""" states = Dataset.states @@ -419,8 +416,6 @@ class DatasetInstance( object ): for child in self.children: child.mark_deleted() - - class HistoryDatasetAssociation( DatasetInstance ): def __init__( self, hid = None, history = None, copied_from_history_dataset_association = None, copied_from_library_folder_dataset_association = None, **kwd ): DatasetInstance.__init__( self, **kwd ) @@ -521,7 +516,7 @@ class Library( object ): self.root_folder = root_folder class LibraryFolder( object ): - def __init__( self, name = None, description = None, order_id = None ): + def __init__( self, name = None, description = None, item_count = 0, order_id = None ): self.name = name or "Unnamed folder" self.description = description self.item_count = item_count diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py index 6a8f982fc81..e6f132693da 100644 --- a/lib/galaxy/model/mapping.py +++ b/lib/galaxy/model/mapping.py @@ -139,9 +139,7 @@ GroupDatasetAssociation.table = Table( "group_dataset_association", metadata, Column( "update_time", DateTime, default=now, onupdate=now ), Column( "permitted_actions", JSONType(), default=[] ) ) -# TODO, Nate: Need to better understand what these Default tables are for and add appropriate -# comments here to clarify them. Need to ensure that they should include the permitted_actions -# columns, and if so, that they are correctly populated. +# The following table stores the permissions that are considered the defaults for new histories when they are created by a user DefaultUserGroupAssociation.table = Table( "default_user_group_association", metadata, Column( "id", Integer, primary_key=True ), Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ), @@ -150,6 +148,8 @@ DefaultUserGroupAssociation.table = Table( "default_user_group_association", met Column( "update_time", DateTime, default=now, onupdate=now ), Column( "permitted_actions", JSONType(), default=[] ) ) +# The following table stores the default permissions assigned to histories for datasets +# that need permissions ( dataset permissions that cannot be determined based on ancestor ) DefaultHistoryGroupAssociation.table = Table( "default_history_group_association", metadata, Column( "id", Integer, primary_key=True ), Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ), @@ -417,10 +417,6 @@ assign_mapper( context, UserGroupAssociation, UserGroupAssociation.table, properties=dict( user=relation( User, backref = "groups" ), group=relation( Group, backref = "users" ) ) ) - -# TODO, Nate: Need to make sure we have optimal performance - may need more mappers... -# if we have a user and a list of datasets, what is the fastest -# way to ask whether the user has a certain action on all of them. assign_mapper( context, GroupDatasetAssociation, GroupDatasetAssociation.table, properties=dict( dataset=relation( Dataset, backref = "groups" ), group=relation( Group, backref = "datasets" ) ) ) @@ -609,7 +605,7 @@ def init( file_path, url, engine_options={}, create_tables=False ): if result.Group.count() == 0: log.warning( "There were no groups located, setting up default (public) group." ) # Create public group - public_group = result.security_agent.create_group( name = 'public' ) + public_group = result.security_agent.create_group( name='public' ) # Store public group id result.security_agent.set_public_group( public_group ) # Loop through all histories and set up rbac on users, histories and datasets diff --git a/lib/galaxy/security/__init__.py b/lib/galaxy/security/__init__.py index 0ba0bf6b621..ed73ec2364e 100644 --- a/lib/galaxy/security/__init__.py +++ b/lib/galaxy/security/__init__.py @@ -13,9 +13,14 @@ log = logging.getLogger(__name__) # are correct when an authenticated user creates things inside their "private" environment. class RBACAgent: """Class that handles galaxy security""" - permitted_actions = Bunch( + permitted_actions = Bunch( + # The ability to edit the metadata of the associated dataset DATASET_EDIT_METADATA = 'dataset_edit_metadata', + # The ability to change the permissions of a dataset (so specifically, to add and modify + # group_dataset_association rows where the dataset is the dataset for which the permission is set). DATASET_MANAGE_PERMISSIONS = 'dataset_manage_permissions', + # The ability to perform any read only operation on the dataset (view, display at external site, + # use in a job, etc). DATASET_ACCESS = 'dataset_access' ) def allow_action( self, user, action, **kwd ): diff --git a/lib/galaxy/web/controllers/admin.py b/lib/galaxy/web/controllers/admin.py index fd9b4be90d8..3367ce6bc36 100644 --- a/lib/galaxy/web/controllers/admin.py +++ b/lib/galaxy/web/controllers/admin.py @@ -1,24 +1,676 @@ +import shutil, StringIO from galaxy.web.base.controller import * -import logging, sets, time +from galaxy.datatypes import sniff +from galaxy.security import RBACAgent +import galaxy.model +from xml.sax.saxutils import escape, unescape +import pkg_resources +pkg_resources.require( "sqlalchemy>=0.3" ) +import sqlalchemy as sa +import logging log = logging.getLogger( __name__ ) +entities = { '@': 'FuNkYaT' } +unentities = { 'FuNkYaT' : '@' } +no_privilege_msg = "You must have Galaxy administrator privileges to use this feature." + class Admin( BaseController ): + def user_is_admin( self, trans ): + admin_users = trans.app.config.get( "admin_users", "" ).split( "," ) + if not admin_users: + return False + user = trans.get_user() + if not user: + return False + if not user.email in admin_users: + return False + return True @web.expose def index( self, trans, **kwd ): - msg = '' - if 'action' in kwd: - if kwd['action'] == "tool_reload": - msg = self.tool_reload( **kwd ) - return trans.fill_template( 'admin_main.mako', toolbox=self.app.toolbox, msg=msg ) - - def tool_reload( self, tool_version=None, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) params = util.Params( kwd ) - if params.passwd==self.app.config.admin_pass: - tool_id = params.tool_id - self.app.toolbox.reload( tool_id ) - msg = 'Reloaded tool: ' + tool_id + msg = params.msg + return trans.fill_template( '/admin/index.mako', msg=msg ) + @web.expose + def reload_tool( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + return trans.fill_template( '/admin/reload_tool.mako', toolbox=self.app.toolbox, msg=msg ) + @web.expose + def tool_reload( self, trans, tool_version=None, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + tool_id = params.tool_id + self.app.toolbox.reload( tool_id ) + msg = 'Reloaded tool: ' + tool_id + return trans.fill_template( '/admin/reload_tool.mako', toolbox=self.app.toolbox, msg=msg ) + @web.expose + def dataset_security( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + return trans.fill_template( '/admin/dataset_security/index.mako', msg=msg ) + + # Galaxy Group Stuff + @web.expose + def groups( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + # This query retrieves groups that are not deleted and members of each group + q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ), + ( galaxy.model.Group.table.c.name ).label( 'group_name' ), + ( galaxy.model.Group.table.c.priority ).label( 'group_priority' ), + sa.func.count( galaxy.model.User.table.c.id ).label( 'total_members' ) ), + whereclause = galaxy.model.Group.table.c.deleted == False, + from_obj = [ sa.outerjoin( galaxy.model.Group.table, + galaxy.model.UserGroupAssociation.table + ).outerjoin( galaxy.model.User.table ) ], + group_by = [ galaxy.model.Group.table.c.id, + galaxy.model.Group.table.c.name, + galaxy.model.Group.table.c.priority ], + order_by = [ galaxy.model.Group.table.c.name ] ) + groups = [] + for row in q.execute(): + # This 2nd query retrieves the number of datasets and dataset permitted_actions associated with each group + q2 = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ), + ( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ), + sa.func.count( galaxy.model.Dataset.table.c.id ).label( 'total_datasets' ) ), + whereclause = galaxy.model.Group.table.c.id == row.group_id, + from_obj = [ sa.outerjoin( galaxy.model.Group.table, + galaxy.model.GroupDatasetAssociation.table + ).outerjoin( galaxy.model.Dataset.table ) ], + group_by = [ galaxy.model.Group.table.c.id, + galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ] ) + for row2 in q2.execute(): + total_datasets = row2.total_datasets + permitted_actions = [] + # There may not yet be any GroupDatasetAssociations, in which case no + # actions will be found + if row2.permitted_actions: + for action in row2.permitted_actions: + permitted_actions.append( action.encode( 'ascii' ) ) + permitted_actions.sort() + groups.append( ( row.group_id, + escape( row.group_name, entities ), + row.group_priority, + row.total_members, + total_datasets, + permitted_actions ) ) + return trans.fill_template( '/admin/dataset_security/groups.mako', + groups=groups, + msg=msg ) + @web.expose + def create_group( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ), + ( galaxy.model.User.table.c.email ).label( 'user_email') ), + from_obj = [ galaxy.model.User.table ], + order_by = [ galaxy.model.User.table.c.email ] ) + users = [] + for row in q.execute(): + users.append( ( row.user_id, + escape( row.user_email, entities ) ) ) + return trans.fill_template( '/admin/dataset_security/group_create.mako', users=users, msg=msg ) + @web.expose + def new_group( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + name = unescape( params.name, unentities ) + if not name: + msg = "Please enter a name" + trans.response.send_redirect( '/admin/create_group?msg=%s' % msg ) else: - msg = 'Invalid password' - return msg + try: + priority = int( params.priority ) + except: + priority = 0 + # Create the group + group = galaxy.model.Group( name, priority ) + group.flush() + # Add the members + members = params.members + for user_id in members: + user = galaxy.model.User.get( user_id ) + # Create the UserGroupAssociation + user_group_association = galaxy.model.UserGroupAssociation( user, group ) + user_group_association.flush() + msg = "The new group has been created with priority %s and %s members" % ( str( priority ), str( len( members ) ) ) + trans.response.send_redirect( '/admin/groups?msg=%s' % msg ) + @web.expose + def group_members( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + group_id = params.group_id + group_name = unescape( params.group_name, unentities ) + # This query retrieves all members of the group + q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ), + ( galaxy.model.User.table.c.email ).label( 'user_email' ) ), + whereclause = galaxy.model.UserGroupAssociation.table.c.group_id == group_id, + from_obj = [ sa.outerjoin( galaxy.model.UserGroupAssociation.table, + galaxy.model.User.table ) ], + order_by = [ 'user_email' ] ) + members = [] + for row in q.execute(): + members.append( ( row.user_id, + escape( row.user_email, entities ) ) ) + return trans.fill_template( '/admin/dataset_security/group_members.mako', + group_id=group_id, + group_name=escape( group_name, entities ), + members=members, + msg=msg ) + @web.expose + def group_members_edit( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + group_id = params.group_id + group_name = unescape( params.group_name, unentities ) + members = params.members + # First get all users + q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ), + ( galaxy.model.User.table.c.email ).label( 'user_email' ) ), + order_by = [ 'user_email' ] ) + users = [] + for row in q.execute(): + users.append( ( row.user_id, + escape( row.user_email, entities ) ) ) + # Then get members of the group + q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ), + ( galaxy.model.User.table.c.email ).label( 'user_email' ) ), + whereclause = galaxy.model.UserGroupAssociation.table.c.group_id == group_id, + from_obj = [ sa.outerjoin( galaxy.model.UserGroupAssociation.table, + galaxy.model.User.table ) ], + order_by = [ 'user_email' ] ) + members = [] + for row in q.execute(): + members.append( ( row.user_id, + escape( row.user_email, entities ) ) ) + return trans.fill_template( '/admin/dataset_security/group_members_edit.mako', + group_id=group_id, + group_name=escape( group_name, entities ), + users=users, + members=members, + msg=msg ) + @web.expose + def update_group_members( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + group_id = int( params.group_id ) + members = params.members + if members and not isinstance( members, list ): + # mako passes singleton lists as strings for some reason + members = [ members ] + # Handle case where admin removed all members from group + elif members is None: + members = [] + group = galaxy.model.Group.get( group_id ) + # This is tricky since we have default association tables with + # records referring to members of this group. Because of this, + # we'll need to handle changes to the member list rather than the + # simpler approach of deleting all existing members and creating + # new records for user_ids in the received members param. + # First remove existing members that are not in the received members param + for user_group_assoc in group.users: + if user_group_assoc.user_id not in members: + user = galaxy.model.User.get( user_group_assoc.user_id ) + # Delete DefaultUserGroupAssociations + for default_user_group_association in user.default_groups: + if default_user_group_association.group_id == group_id: + default_user_group_association.delete() + default_user_group_association.flush() + break # Should only be 1 record + # Delete DefaultHistoryGroupAssociations + for history in user.histories: + for default_history_group_association in history.default_groups: + if default_history_group_association.group_id == group_id: + default_history_group_association.delete() + default_history_group_association.flush() + # Delete the UserGroupAssociation + user_group_assoc.delete() + user_group_assoc.flush() + # Then add all new members to the group + for user_id in members: + user = galaxy.model.User.get( user_id ) + if user not in group.users: + user_group_association = galaxy.model.UserGroupAssociation( user, group ) + user_group_association.flush() + msg = "Group membership has been updated with a total of %s members" % len( members ) + trans.response.send_redirect( '/admin/group_members?group_id=%s&group_name=%s&msg=%s' % ( str( group_id ), params.group_name, msg ) ) + @web.expose + def group_dataset_permitted_actions( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + group_id = int( params.group_id ) + group_name = unescape( params.group_name, unentities ) + # Need to get all actions to send to the form + dataset_actions = [] + dpas = RBACAgent.permitted_actions + for dpa in dpas.items(): + if dpa[0].startswith( 'DATASET' ): + dataset_actions.append( dpa[1] ) + dataset_actions.sort() + q = sa.select( ( ( galaxy.model.Group.table.c.priority ).label( 'group_priority' ), + ( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ) ), + whereclause = galaxy.model.GroupDatasetAssociation.table.c.id == group_id, + from_obj = [ sa.outerjoin( galaxy.model.Group.table, + galaxy.model.GroupDatasetAssociation.table ) ] ) + gdas = [] + for row in q.execute(): + permitted_actions = [] + # Although there may be GroupDatasetAssociations, there may not be any permitted_actions on them + if row.permitted_actions: + for action in row.permitted_actions: + permitted_actions.append( action.encode( 'ascii' ) ) + permitted_actions.sort() + gdas.append( ( row.group_priority, + permitted_actions ) ) + break # Just need 1 row + return trans.fill_template( '/admin/dataset_security/group_dataset_permitted_actions_edit.mako', + group_id=group_id, + group_name=escape( group_name, entities ), + gdas=gdas, + dataset_actions=dataset_actions, + msg=msg ) + @web.expose + def group_dataset_permitted_actions_edit( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + group_id = int( params.group_id ) + actions = params.actions + if actions and not isinstance( actions, list ): + actions = [ actions ] + # Update the permitted_actions for every GroupDatasetAssociation of the Group + q = sa.update( galaxy.model.GroupDatasetAssociation.table, + whereclause = galaxy.model.GroupDatasetAssociation.table.c.group_id == group_id, + values = { galaxy.model.GroupDatasetAssociation.table.c.permitted_actions : actions } ) + result = q.execute() + msg = "The dataset permitted actions for the group have been updated, affecting %d rows in the group_dataset_association table" % result.rowcount + trans.response.send_redirect( '/admin/groups?msg=%s' % msg ) + @web.expose + def mark_group_deleted( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + group_id = params.group_id + group = galaxy.model.Group.get( group_id ) + group.deleted = True + group.flush() + msg = "The group has been marked as deleted." + trans.response.send_redirect( '/admin/groups?msg=%s' % msg ) + @web.expose + def deleted_groups( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + # This query retrieves groups that are not deleted and members of each group + q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ), + ( galaxy.model.Group.table.c.name ).label( 'group_name' ), + ( galaxy.model.Group.table.c.priority ).label( 'group_priority' ), + sa.func.count( galaxy.model.User.table.c.id ).label( 'total_members' ) ), + whereclause = galaxy.model.Group.table.c.deleted == True, + from_obj = [ sa.outerjoin( galaxy.model.Group.table, + galaxy.model.UserGroupAssociation.table + ).outerjoin( galaxy.model.User.table ) ], + group_by = [ galaxy.model.Group.table.c.id, + galaxy.model.Group.table.c.name, + galaxy.model.Group.table.c.priority ], + order_by = [ galaxy.model.Group.table.c.name ] ) + groups = [] + for row in q.execute(): + # This 2nd query retrieves the number of datasets and dataset permitted_actions associated with each group + q2 = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ), + ( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ), + sa.func.count( galaxy.model.Dataset.table.c.id ).label( 'total_datasets' ) ), + whereclause = galaxy.model.Group.table.c.id == row.group_id, + from_obj = [ sa.outerjoin( galaxy.model.Group.table, + galaxy.model.GroupDatasetAssociation.table + ).outerjoin( galaxy.model.Dataset.table ) ], + group_by = [ galaxy.model.Group.table.c.id, + galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ] ) + for row2 in q2.execute(): + total_datasets = row2.total_datasets + permitted_actions = [] + # There may not yet be any GroupDatasetAssociations, in which case no + # actions will be found + if row2.permitted_actions: + for action in row2.permitted_actions: + permitted_actions.append( action.encode( 'ascii' ) ) + permitted_actions.sort() + groups.append( ( row.group_id, + escape( row.group_name, entities ), + row.group_priority, + row.total_members, + total_datasets, + permitted_actions ) ) + return trans.fill_template( '/admin/dataset_security/deleted_groups.mako', + groups=groups, + msg=msg ) + @web.expose + def undelete_group( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + group_id = params.group_id + group = galaxy.model.Group.get( group_id ) + group.deleted = False + group.flush() + msg = "The group has been marked as not deleted." + trans.response.send_redirect( '/admin/groups?msg=%s' % msg ) + @web.expose + def purge_group( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + group_id = params.group_id + group = galaxy.model.Group.get( group_id ) + # Remove members and all associations + for user_group_assoc in group.users: + user = galaxy.model.User.get( user_group_assoc.user_id ) + # Delete DefaultUserGroupAssociations + for default_user_group_association in user.default_groups: + if default_user_group_association.group_id == group_id: + default_user_group_association.delete() + default_user_group_association.flush() + break # Should only be 1 record + # Delete DefaultHistoryGroupAssociations + for history in user.histories: + for default_history_group_association in history.default_groups: + if default_history_group_association.group_id == group_id: + default_history_group_association.delete() + default_history_group_association.flush() + # Delete the UserGroupAssociation + user_group_assoc.delete() + user_group_assoc.flush() + # Delete the Group + group.delete() + group.flush() + msg = "The group has been purged from the database." + trans.response.send_redirect( '/admin/deleted_groups?msg=%s' % msg ) + + # Galaxy User Stuff + @web.expose + def users( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ), + ( galaxy.model.User.table.c.email ).label( 'user_email') ), + from_obj = [ galaxy.model.User.table ], + order_by = [ galaxy.model.User.table.c.email ] ) + users = [] + for row in q.execute(): + users.append( ( row.user_id, + escape( row.user_email, entities ) ) ) + return trans.fill_template( '/admin/dataset_security/users.mako', + users=users, + msg=msg ) + @web.expose + def specified_users_groups( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + params = util.Params( kwd ) + msg = params.msg + user_id = int( params.user_id ) + user_email = unescape( params.user_email, unentities ) + # Get the groups to which the user belongs + q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ), + ( galaxy.model.Group.table.c.name ).label( 'group_name' ), + ( galaxy.model.Group.table.c.priority ).label( 'group_priority' ) ), + whereclause = galaxy.model.User.table.c.id == user_id, + from_obj = [ sa.outerjoin( galaxy.model.User.table, + galaxy.model.UserGroupAssociation.table ).outerjoin( galaxy.model.Group.table ) ], + order_by = [ 'group_name' ] ) + groups = [] + for row in q.execute(): + # Perform a 2nd query to get datasets associated with each group + q2 = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ), + ( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ), + sa.func.count( galaxy.model.Dataset.table.c.id ).label( 'total_datasets' ) ), + whereclause = galaxy.model.Group.table.c.id == row.group_id, + from_obj = [ sa.outerjoin( galaxy.model.Group.table, + galaxy.model.GroupDatasetAssociation.table + ).outerjoin( galaxy.model.Dataset.table ) ], + group_by = [ galaxy.model.Group.table.c.id, + galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ] ) + for row2 in q2.execute(): + total_datasets = row2.total_datasets + permitted_actions = [] + # There may not yet be any GroupDatasetAssociations, in which case no + # actions will be found + if row2.permitted_actions: + for action in row2.permitted_actions: + permitted_actions.append( action.encode( 'ascii' ) ) + permitted_actions.sort() + groups.append( ( row.group_id, + escape( row.group_name, entities ), + row.group_priority, + row2.total_datasets, + permitted_actions ) ) + return trans.fill_template( '/admin/dataset_security/specified_users_groups.mako', + user_id=user_id, + user_email=escape( user_email, entities ), + groups=groups, + msg=msg ) + + # Galaxy Library Stuff + @web.expose + def libraries( self, trans, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + return trans.fill_template( '/admin/library/libraries.mako', libraries=trans.app.model.Library.select() ) + @web.expose + def library( self, trans, id=None, name="Unnamed", description=None, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + if 'create_library' in kwd: + library = trans.app.model.Library( name=name, description=description ) + root_folder = trans.app.model.LibraryFolder( name=name, description=description ) + root_folder.flush() + library.root_folder = root_folder + library.flush() + trans.response.send_redirect( web.url_for( action='folder', id = root_folder.id ) ) + elif id is None: + return trans.show_form( + web.FormBuilder( action = web.url_for(), title = "Create a new Library", name = "create_library", submit_text = "Submit" ) + .add_text( name = "name", label = "Name", value = "Unnamed", error = None, help = None ) + .add_text( name = "description", label = "Description", value = None, error = None, help = None ) + .add_input( 'hidden', "Create Library", 'create_library', use_label = False ) ) + library = trans.app.model.Library.get( id ) + if library: + return trans.fill_template( '/admin/library/library.mako', library = library ) + else: + return trans.show_error_message( "Invalid library specified" ) + @web.expose + def folder( self, trans, id=None, name="Unnamed", description=None, parent_id = None, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + if 'create_folder' in kwd: + folder = trans.app.model.LibraryFolder( name = name, description = description ) + if parent_id: + parent_folder = trans.app.model.LibraryFolder.get( parent_id ) + parent_folder.add_folder( folder ) + folder.flush() + trans.response.send_redirect( web.url_for( action='folder', id = folder.id ) ) + elif id is None: + return trans.show_form( + web.FormBuilder( action = web.url_for(), title = "Create a new Folder", name = "create_folder", submit_text = "Submit" ) + .add_text( name = "name", label = "Name", value = "Unnamed", error = None, help = None ) + .add_text( name = "description", label = "Description", value = None, error = None, help = None ) + .add_input( 'hidden', None, 'parent_id', value = parent_id, use_label = False ) + .add_input( 'hidden', "Create Folder", 'create_folder', use_label = False ) ) + folder = trans.app.model.LibraryFolder.get( id ) + if folder: + msg = '' + if 'rename_folder' in kwd: + folder.name = name + folder.description = description + folder.flush() + msg = 'Folder has been renamed.' + return trans.fill_template( '/admin/library/folder.mako', folder=folder, msg=msg ) + else: + return trans.show_error_message( "Invalid folder specified" ) + @web.expose + def dataset( self, trans, id=None, name="Unnamed", info='no info', extension=None, folder_id=None, dbkey=None, **kwd ): + if not self.user_is_admin( trans ): + return trans.show_error_message( no_privilege_msg ) + data_files = [] + def add_file( file_obj, name, extension, dbkey, info = 'no info', space_to_tab = False ): + data_type = None + temp_name = sniff.stream_to_file( file_obj ) + if space_to_tab: + line_count = sniff.convert_newlines_sep2tabs( temp_name ) + else: + line_count = sniff.convert_newlines( temp_name ) + if extension == 'auto': + data_type = sniff.guess_ext( temp_name, sniff_order=trans.app.datatypes_registry.sniff_order ) + else: + data_type = extension + dataset = trans.app.model.LibraryFolderDatasetAssociation( name = name, info = info, extension = data_type, dbkey = dbkey, create_dataset = True ) + folder = trans.app.model.LibraryFolder.get( folder_id ) + folder.add_dataset( dataset ) + dataset.flush() + # TODO, SET SECURTY INTERACTIVELY ON DATASET, right now everything is public + trans.app.security_agent.set_dataset_groups( dataset.dataset, [trans.app.security_agent.get_public_group()] ) + shutil.move( temp_name, dataset.dataset.file_name ) + dataset.dataset.state = dataset.dataset.states.OK + dataset.init_meta() + if line_count is not None: + try: + dataset.set_peek( line_count=line_count ) + except: + dataset.set_peek() + else: + dataset.set_peek() + dataset.set_size() + + if dataset.missing_meta(): + dataset.datatype.set_meta( dataset ) + trans.app.model.flush() + + return dataset + if 'create_dataset' in kwd: + #copied from upload tool action + last_dataset_created = None + data_file = kwd['file_data'] + url_paste = kwd['url_paste'] + space_to_tab = False + if 'space_to_tab' in kwd: + if kwd['space_to_tab'] not in ["None", None]: + space_to_tab = True + temp_name = "" + data_list = [] + + if 'filename' in dir( data_file ): + file_name = data_file.filename + file_name = file_name.split( '\\' )[-1] + file_name = file_name.split( '/' )[-1] + last_dataset_created = add_file( data_file.file, file_name, extension, dbkey, info="uploaded file", space_to_tab = space_to_tab ) + elif url_paste not in [ None, "" ]: + if url_paste.lower().find( 'http://' ) >= 0 or url_paste.lower().find( 'ftp://' ) >= 0: + url_paste = url_paste.replace( '\r', '' ).split( '\n' ) + for line in url_paste: + line = line.rstrip( '\r\n' ) + if line: + last_dataset_created = add_file( urllib.urlopen( line ), line, extension, dbkey, info="uploaded url", space_to_tab=space_to_tab ) + else: + is_valid = False + for line in url_paste: + line = line.rstrip( '\r\n' ) + if line: + is_valid = True + break + if is_valid: + last_dataset_created = add_file( StringIO.StringIO( url_paste ), 'Pasted Entry', extension, dbkey, info="pasted entry", space_to_tab=space_to_tab ) + trans.response.send_redirect( web.url_for( action='dataset', id = last_dataset_created.id ) ) + #return self.dataset( trans, id = last_dataset_created.id ) + elif id is None: + return trans.fill_template( '/admin/library/new_dataset.mako', folder_id = folder_id ) + dataset = trans.app.model.LibraryFolderDatasetAssociation.get( id ) + if dataset: + #copied from edit attributes for 'regular' datasets + p = util.Params(kwd, safe=False) + if p.change: + # The user clicked the Save button on the 'Change data type' form + trans.app.datatypes_registry.change_datatype( dataset, p.datatype ) + trans.app.model.flush() + elif p.save: + # The user clicked the Save button on the 'Edit Attributes' form + dataset.name = name + dataset.info = info + + # The following for loop will save all metadata_spec items + for name, spec in dataset.datatype.metadata_spec.items(): + if spec.get("readonly"): + continue + optional = p.get("is_"+name, None) + if optional and optional == 'true': + # optional element... == 'true' actually means it is NOT checked (and therefore ommitted) + setattr(dataset.metadata,name,None) + else: + setattr(dataset.metadata,name,spec.unwrap(p.get(name, None), p)) + + dataset.datatype.after_edit( dataset ) + trans.app.model.flush() + return trans.show_ok_message( "Attributes updated" ) + elif p.detect: + # The user clicked the Auto-detect button on the 'Edit Attributes' form + for name, spec in dataset.datatype.metadata_spec.items(): + # We need to be careful about the attributes we are resetting + if name != 'name' and name != 'info' and name != 'dbkey': + if spec.get( 'default' ): + setattr( dataset.metadata,name,spec.unwrap( spec.get( 'default' ), spec )) + dataset.datatype.set_meta( dataset ) + dataset.datatype.after_edit( dataset ) + trans.app.model.flush() + return trans.show_ok_message( "Attributes updated" ) + + dataset.datatype.before_edit( dataset ) + + if "dbkey" in dataset.datatype.metadata_spec and not dataset.metadata.dbkey: + # Copy dbkey into metadata, for backwards compatability + # This looks like it does nothing, but getting the dbkey + # returns the metadata dbkey unless it is None, in which + # case it resorts to the old dbkey. Setting the dbkey + # sets it properly in the metadata + dataset.metadata.dbkey = dataset.dbkey + metadata = list() + # a list of MetadataParemeters + for name, spec in dataset.datatype.metadata_spec.items(): + if spec.visible: + metadata.append( spec.wrap( dataset.metadata.get(name), dataset ) ) + # let's not overwrite the imported datatypes module with the variable datatypes? + ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()] + ldatatypes.sort() + return trans.fill_template( "/admin/library/dataset.mako", + dataset=dataset, + metadata=metadata, + datatypes=ldatatypes, + err=None ) + else: + return trans.show_error_message( "Invalid dataset specified" ) diff --git a/lib/galaxy/web/controllers/library.py b/lib/galaxy/web/controllers/library.py index c8b1f6f0573..94e7b5731e0 100644 --- a/lib/galaxy/web/controllers/library.py +++ b/lib/galaxy/web/controllers/library.py @@ -1,12 +1,10 @@ from galaxy.web.base.controller import * -from galaxy.datatypes import sniff -import logging, shutil, StringIO +import logging log = logging.getLogger( __name__ ) class Library( BaseController ): - @web.expose def index( self, trans, library_id = None, import_ids = [], **kwd ): #use for importing an entry into your history @@ -21,198 +19,5 @@ class Library( BaseController ): history.flush() return trans.show_ok_message( "%i datasets have been imported into your history" % len( import_ids ), refresh_frames=['history'] ) elif library_id: - return trans.fill_template( 'library/user_view_library.mako', library = trans.app.model.Library.get( library_id ) ) - return trans.fill_template( 'library/user_list_libraries.mako', libraries = trans.app.model.Library.select() ) - - #make admin only - @web.expose - def manage_libraries( self, trans, **kwd ): - return trans.fill_template( 'library/admin_list_libraries.mako', libraries = trans.app.model.Library.select() ) - - #make admin only - @web.expose - def manage_library( self, trans, id=None, name="Unnamed", description=None, **kwd ): - if 'create_library' in kwd: - library = trans.app.model.Library( name = name, description = description ) - root_folder = trans.app.model.LibraryFolder( name = name, description = description ) - root_folder.flush() - library.root_folder = root_folder - library.flush() - trans.response.send_redirect( web.url_for( action='manage_folder', id = root_folder.id ) ) - elif id is None: - return trans.show_form( - web.FormBuilder( action = web.url_for(), title = "Create a new Library", name = "create_library", submit_text = "Submit" ) - .add_text( name = "name", label = "Name", value = "Unnamed", error = None, help = None ) - .add_text( name = "description", label = "Description", value = None, error = None, help = None ) - .add_input( 'hidden', "Create Library", 'create_library', use_label = False ) ) - library = trans.app.model.Library.get( id ) - if library: - return trans.fill_template( 'library/manage_library.mako', library = library ) - else: - return trans.show_error_message( "Invalid library specified" ) - - #make admin only - @web.expose - def manage_folder( self, trans, id=None, name="Unnamed", description=None, parent_id = None, **kwd ): - if 'create_folder' in kwd: - folder = trans.app.model.LibraryFolder( name = name, description = description ) - if parent_id: - parent_folder = trans.app.model.LibraryFolder.get( parent_id ) - parent_folder.add_folder( folder ) - folder.flush() - trans.response.send_redirect( web.url_for( action='manage_folder', id = folder.id ) ) - elif id is None: - return trans.show_form( - web.FormBuilder( action = web.url_for(), title = "Create a new Folder", name = "create_folder", submit_text = "Submit" ) - .add_text( name = "name", label = "Name", value = "Unnamed", error = None, help = None ) - .add_text( name = "description", label = "Description", value = None, error = None, help = None ) - .add_input( 'hidden', None, 'parent_id', value = parent_id, use_label = False ) - .add_input( 'hidden', "Create Folder", 'create_folder', use_label = False ) ) - folder = trans.app.model.LibraryFolder.get( id ) - if folder: - msg = '' - if 'rename_folder' in kwd: - folder.name = name - folder.description = description - folder.flush() - msg = 'Folder has been renamed.' - return trans.fill_template( 'library/manage_folder.mako', folder = folder, msg = msg ) - else: - return trans.show_error_message( "Invalid folder specified" ) - - - #make admin only - @web.expose - def manage_dataset( self, trans, id=None, name="Unnamed", info = 'no info', extension = None, folder_id = None, dbkey = None, **kwd ): - data_files = [] - def add_file( file_obj, name, extension, dbkey, info = 'no info', space_to_tab = False ): - data_type = None - temp_name = sniff.stream_to_file( file_obj ) - if space_to_tab: - line_count = sniff.convert_newlines_sep2tabs( temp_name ) - else: - line_count = sniff.convert_newlines( temp_name ) - if extension == 'auto': - data_type = sniff.guess_ext( temp_name, sniff_order=trans.app.datatypes_registry.sniff_order ) - else: - data_type = extension - dataset = trans.app.model.LibraryFolderDatasetAssociation( name = name, info = info, extension = data_type, dbkey = dbkey, create_dataset = True ) - folder = trans.app.model.LibraryFolder.get( folder_id ) - folder.add_dataset( dataset ) - dataset.flush() - # TODO, SET SECURTY INTERACTIVELY ON DATASET, right now everything is public - trans.app.security_agent.set_dataset_groups( dataset.dataset, [trans.app.security_agent.get_public_group()] ) - shutil.move( temp_name, dataset.dataset.file_name ) - dataset.dataset.state = dataset.dataset.states.OK - dataset.init_meta() - if line_count is not None: - try: - dataset.set_peek( line_count=line_count ) - except: - dataset.set_peek() - else: - dataset.set_peek() - dataset.set_size() - - if dataset.missing_meta(): - dataset.datatype.set_meta( dataset ) - trans.app.model.flush() - - return dataset - if 'create_dataset' in kwd: - #copied from upload tool action - last_dataset_created = None - data_file = kwd['file_data'] - url_paste = kwd['url_paste'] - space_to_tab = False - if 'space_to_tab' in kwd: - if kwd['space_to_tab'] not in ["None", None]: - space_to_tab = True - temp_name = "" - data_list = [] - - if 'filename' in dir( data_file ): - file_name = data_file.filename - file_name = file_name.split( '\\' )[-1] - file_name = file_name.split( '/' )[-1] - last_dataset_created = add_file( data_file.file, file_name, extension, dbkey, info="uploaded file", space_to_tab = space_to_tab ) - elif url_paste not in [ None, "" ]: - if url_paste.lower().find( 'http://' ) >= 0 or url_paste.lower().find( 'ftp://' ) >= 0: - url_paste = url_paste.replace( '\r', '' ).split( '\n' ) - for line in url_paste: - line = line.rstrip( '\r\n' ) - if line: - last_dataset_created = add_file( urllib.urlopen( line ), line, extension, dbkey, info="uploaded url", space_to_tab=space_to_tab ) - else: - is_valid = False - for line in url_paste: - line = line.rstrip( '\r\n' ) - if line: - is_valid = True - break - if is_valid: - last_dataset_created = add_file( StringIO.StringIO( url_paste ), 'Pasted Entry', extension, dbkey, info="pasted entry", space_to_tab=space_to_tab ) - trans.response.send_redirect( web.url_for( action='manage_dataset', id = last_dataset_created.id ) ) - #return self.manage_dataset( trans, id = last_dataset_created.id ) - elif id is None: - return trans.fill_template( 'library/new_dataset.mako', folder_id = folder_id ) - dataset = trans.app.model.LibraryFolderDatasetAssociation.get( id ) - if dataset: - #copied from edit attributes for 'regular' datasets - p = util.Params(kwd, safe=False) - if p.change: - # The user clicked the Save button on the 'Change data type' form - trans.app.datatypes_registry.change_datatype( dataset, p.datatype ) - trans.app.model.flush() - elif p.save: - # The user clicked the Save button on the 'Edit Attributes' form - dataset.name = name - dataset.info = info - - # The following for loop will save all metadata_spec items - for name, spec in dataset.datatype.metadata_spec.items(): - if spec.get("readonly"): - continue - optional = p.get("is_"+name, None) - if optional and optional == 'true': - # optional element... == 'true' actually means it is NOT checked (and therefore ommitted) - setattr(dataset.metadata,name,None) - else: - setattr(dataset.metadata,name,spec.unwrap(p.get(name, None), p)) - - dataset.datatype.after_edit( dataset ) - trans.app.model.flush() - return trans.show_ok_message( "Attributes updated" ) - elif p.detect: - # The user clicked the Auto-detect button on the 'Edit Attributes' form - for name, spec in dataset.datatype.metadata_spec.items(): - # We need to be careful about the attributes we are resetting - if name != 'name' and name != 'info' and name != 'dbkey': - if spec.get( 'default' ): - setattr( dataset.metadata,name,spec.unwrap( spec.get( 'default' ), spec )) - dataset.datatype.set_meta( dataset ) - dataset.datatype.after_edit( dataset ) - trans.app.model.flush() - return trans.show_ok_message( "Attributes updated" ) - - dataset.datatype.before_edit( dataset ) - - if "dbkey" in dataset.datatype.metadata_spec and not dataset.metadata.dbkey: - # Copy dbkey into metadata, for backwards compatability - # This looks like it does nothing, but getting the dbkey - # returns the metadata dbkey unless it is None, in which - # case it resorts to the old dbkey. Setting the dbkey - # sets it properly in the metadata - dataset.metadata.dbkey = dataset.dbkey - metadata = list() - # a list of MetadataParemeters - for name, spec in dataset.datatype.metadata_spec.items(): - if spec.visible: - metadata.append( spec.wrap( dataset.metadata.get(name), dataset ) ) - # let's not overwrite the imported datatypes module with the variable datatypes? - ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()] - ldatatypes.sort() - return trans.fill_template( "/library/manage_dataset.mako", dataset=dataset, metadata=metadata, - datatypes=ldatatypes, err=None ) - else: - return trans.show_error_message( "Invalid dataset specified" ) + return trans.fill_template( '/library/library.mako', library=trans.app.model.Library.get( library_id ) ) + return trans.fill_template( '/library/libraries.mako', libraries=trans.app.model.Library.select() ) diff --git a/templates/admin/dataset_security/group_create.mako b/templates/admin/dataset_security/group_create.mako new file mode 100644 index 00000000000..28596e03fb3 --- /dev/null +++ b/templates/admin/dataset_security/group_create.mako @@ -0,0 +1,96 @@ +<%inherit file="/base.mako"/> + +<% + from galaxy.web.controllers.admin import entities, unentities + from xml.sax.saxutils import escape, unescape +%> + +<%def name="title()">Create Group +
+
+ Libraries  |   + Groups  |   + Users +
+

Create Group

+ + %if msg: + + %endif + + + +

${msg}

+ + + + + + %if len( users ) == 0: + + %else: + + + + + + + %else: + + %endif + + <% ctr += 1 %> + %endfor + + + %endif + + +
Name:   Priority:
There are no Galaxy users
Add Members to Group - Quick Find
+ |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 +
+ <% + ctr = 0 + 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' + %> + %for user in users: + <% email = unescape( user[1], unentities ) %> + %if not email.upper().startswith( curr_anchor ): + <% anchored = False %> + %endif + %if ctr % 2 == 1: +
+ %if email.upper().startswith( curr_anchor ): + %if not anchored: +

+ <% anchored = True %> + %endif + ${email} + %else: + %for anchor in anchors[ anchor_loc: ]: + %if email.upper().startswith( anchor ): + %if not anchored: +

+ <% + curr_anchor = anchor + anchored = True + %> + %endif + ${email} + <% + anchor_loc = anchors.index( anchor ) + break + %> + %endif + %endfor + %endif +
+
+
diff --git a/templates/admin/dataset_security/group_dataset_permitted_actions_edit.mako b/templates/admin/dataset_security/group_dataset_permitted_actions_edit.mako new file mode 100644 index 00000000000..58406d7edbf --- /dev/null +++ b/templates/admin/dataset_security/group_dataset_permitted_actions_edit.mako @@ -0,0 +1,71 @@ +<%inherit file="/base.mako"/> + +<% + from galaxy.web.controllers.admin import entities, unentities + from xml.sax.saxutils import escape, unescape +%> + +<% gn = unescape( group_name, unentities ) %> + +<%def name="title()">Permitted Actions on Datasets +
+
+ Libraries  |   + Groups  |   + Users +
+

Manage Permitted Actions on Datasets for Group '${gn}'

+ + %if msg: + + %endif + + %if len( gdas ) == 0: + + %else: + + + + + + <% ctr = 0 %> + + %for gda in gdas: + %if ctr % 2 == 1: + + %else: + + %endif + + + + + <% ctr += 1 %> + %endfor + + + %endif +

${msg}

 
There is no Galaxy group named '${gn}'
GroupPriorityPermitted Actions on Datasets
${gn}${gda[0]} + %for da in dataset_actions: + <% check = False %> + %for action in gda[1]: + %if action == da: + <% + check = True + break + %> + %endif + %endfor + %if check: + + %else: + + %endif + ${da}
+ %endfor +
+
+
diff --git a/templates/admin/dataset_security/group_members.mako b/templates/admin/dataset_security/group_members.mako new file mode 100644 index 00000000000..36e407959f6 --- /dev/null +++ b/templates/admin/dataset_security/group_members.mako @@ -0,0 +1,47 @@ +<%inherit file="/base.mako"/> + +<% + from galaxy.web.controllers.admin import entities, unentities + from xml.sax.saxutils import escape, unescape +%> + +<% gn = unescape( group_name, unentities ) %> + +<%def name="title()">Create Group +
+
+ Libraries  |   + Groups  |   + Users +
+ +
Members of Group '${gn}'
+ + %if msg: + + %endif + + %if len( members ) == 0: + + %else: + <% ctr = 0 %> + %for member in members: + <% email = unescape( member[1], unentities ) %> + %if ctr % 2 == 1: + + %else: + + %endif + + + <% ctr += 1 %> + %endfor + %endif +

${msg}

 
Group '${gn}' contains no members
${email}
+
diff --git a/templates/admin/dataset_security/group_members_edit.mako b/templates/admin/dataset_security/group_members_edit.mako new file mode 100644 index 00000000000..c7defaa7697 --- /dev/null +++ b/templates/admin/dataset_security/group_members_edit.mako @@ -0,0 +1,114 @@ +<%inherit file="/base.mako"/> + +<% + from galaxy.web.controllers.admin import entities, unentities + from xml.sax.saxutils import escape, unescape +%> + +<%def name="title()">Manage Group Membership +
+
+ Libraries  |   + Groups  |   + Users +
+ + %if msg: + + %endif + <% gn = unescape( group_name, unentities ) %> + + + + +

${msg}

 
+ + + %if len( users ) == 0: + + %else: + + + + + + + %else: + + %endif + + <% ctr += 1 %> + %endfor + + + %endif + + +
There are no Galaxy users
Members of '${gn}' - Quick Find
+ |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 +
+ <% + ctr = 0 + 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' + %> + %for user in users: + <% + email = unescape( user[1], unentities ) + check = False + %> + %for member in members: + <% member_email = unescape( member[1], unentities ) %> + %if email == member_email: + <% + check = True + break + %> + %endif + %endfor + %if not email.upper().startswith( curr_anchor ): + <% anchored = False %> + %endif + %if ctr % 2 == 1: +
+ %if email.upper().startswith( curr_anchor ): + %if not anchored: +

+ <% anchored = True %> + %endif + %if check: + ${email} + %else: + ${email} + %endif + %else: + %for anchor in anchors[ anchor_loc: ]: + %if email.upper().startswith( anchor ): + %if not anchored: +

+ <% + curr_anchor = anchor + anchored = True + %> + %endif + %if check: + ${email} + %else: + ${email} + %endif + <% + anchor_loc = anchors.index( anchor ) + break + %> + %endif + %endfor + %endif +
+
+
diff --git a/templates/admin/dataset_security/groups.mako b/templates/admin/dataset_security/groups.mako new file mode 100644 index 00000000000..0687802932e --- /dev/null +++ b/templates/admin/dataset_security/groups.mako @@ -0,0 +1,67 @@ +<%inherit file="/base.mako"/> + +<% + from galaxy.web.controllers.admin import entities, unentities + from xml.sax.saxutils import escape, unescape +%> + +<%def name="title()">Groups +
+
+ Libraries  |   + Users +
+ +

Groups

+ + %if msg: + + %endif + %if len( groups ) == 0: + + %else: + + + + + + + + + <% ctr = 0 %> + %for group in groups: + <% group_name = unescape( group[1], unentities ) %> + %if ctr % 2 == 1: + + %else: + + %endif + + + + %if group[4] > 0: + + %else: + + %endif + + + + <% ctr += 1 %> + %endfor + %endif +

${msg}

There are no Galaxy groups
GroupPriorityMembersDatasetsGroup Permitted Actions on Datasets 
${group_name}${group[2]}${group[3]}${group[4]}${group[4]} + %if len( group[5] ) == 1: + ${group[5][0]} + %elif len( group[5] ) > 1: + %for da in group[5]: + ${da}
+ %endfor + %endif +
Mark group deleted
+
diff --git a/templates/admin/dataset_security/index.mako b/templates/admin/dataset_security/index.mako new file mode 100644 index 00000000000..1a013ec1fc6 --- /dev/null +++ b/templates/admin/dataset_security/index.mako @@ -0,0 +1,13 @@ +<%inherit file="/base.mako"/> + +<%def name="title()">Dataset Security +
+
Dataset Security
+ + %if msg: + + %endif + + +

${msg}

Groups
Users
+
diff --git a/templates/admin/dataset_security/specified_users_groups.mako b/templates/admin/dataset_security/specified_users_groups.mako new file mode 100644 index 00000000000..8a392a67e31 --- /dev/null +++ b/templates/admin/dataset_security/specified_users_groups.mako @@ -0,0 +1,56 @@ +<%inherit file="/base.mako"/> + +<% + from galaxy.web.controllers.admin import entities, unentities + from xml.sax.saxutils import escape, unescape +%> + +<% email = unescape( user_email, unentities ) %> + +<%def name="title()">Create Group +
+
+ Libraries  |   + Groups  |   + Users +
+

Groups of which '${email}' is a member

+ + %if msg: + + %endif + %if len( groups ) == 0: + + %else: + + + + + + + <% ctr = 0 %> + %for group in groups: + <% gn = unescape( group[1], unentities ) %> + %if ctr % 2 == 1: + + %else: + + %endif + + + %if group[3] > 0: + + %else: + + %endif + + + <% ctr += 1 %> + %endfor + %endif +

${msg}

User '${email}' belongs to no groups
GroupPriorityDatasetsPermitted Actions on Datasets
${gn}${group[2]}${group[3]}${group[3]} + %for da in group[4]: + ${da}
+ %endfor +
+
diff --git a/templates/admin/dataset_security/users.mako b/templates/admin/dataset_security/users.mako new file mode 100644 index 00000000000..22abae62d54 --- /dev/null +++ b/templates/admin/dataset_security/users.mako @@ -0,0 +1,80 @@ +<%inherit file="/base.mako"/> + +<% + from galaxy.web.controllers.admin import entities, unentities + from xml.sax.saxutils import escape, unescape +%> + +<%def name="title()">Users +
+
+ Groups  |   + Libraries +
+ + %if msg: + + %endif + + %if len( users ) == 0: + + %else: + + + + + <% + ctr = 0 + 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' + %> + %for user in users: + <% email = unescape( user[1], unentities ) %> + %if not email.upper().startswith( curr_anchor ): + <% anchored = False %> + %endif + %if ctr % 2 == 1: + + %else: + + %endif + + <% ctr += 1 %> + %endfor + + %endif +

${msg}

 
There are no Galaxy users
Galaxy Users - Quick Find
+ |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 +
+ %if email.upper().startswith( curr_anchor ): + %if not anchored: +

+ <% anchored = True %> + %endif + ${email} + %else: + %for anchor in anchors[ anchor_loc: ]: + %if email.upper().startswith( anchor ): + %if not anchored: +

+ <% + curr_anchor = anchor + anchored = True + %> + %endif + ${email} + <% + anchor_loc = anchors.index( anchor ) + break + %> + %endif + %endfor + %endif +
+
diff --git a/templates/admin/index.mako b/templates/admin/index.mako new file mode 100644 index 00000000000..84e90c84b55 --- /dev/null +++ b/templates/admin/index.mako @@ -0,0 +1,13 @@ +<%inherit file="/base.mako"/> + +
+

Galaxy Administration

+ + %if msg: + + %endif + + + +

${msg}

Dataset Security
Libraries
Reload a tool while the Galaxy server is running
+
diff --git a/templates/admin/library/dataset.mako b/templates/admin/library/dataset.mako new file mode 100644 index 00000000000..52a35099045 --- /dev/null +++ b/templates/admin/library/dataset.mako @@ -0,0 +1,85 @@ +<%inherit file="/base.mako"/> + +<%def name="title()">Edit Dataset Attributes +<%def name="datatype( dataset, datatypes )"> + + +
+
Edit Attributes
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+
+
+ %for element in metadata: +
+ +
+ ${element.get_html()} +
+
+
+ %endfor +
+ +
+
+
+ +
+ +
+
+ This will inspect the dataset and attempt to correct the above column values + if they are not accurate. +
+
+
+
+

+

+
Change data type
+
+
+ +
+ +
+ ${datatype( dataset, datatypes )} +
+
+ This will change the datatype of the existing dataset + but not modify its contents. Use this if Galaxy + has incorrectly guessed the type of your dataset. +
+
+
+
+ +
+
+
+
+manage containing folder +

diff --git a/templates/admin/library/folder.mako b/templates/admin/library/folder.mako new file mode 100644 index 00000000000..b4b11c74b36 --- /dev/null +++ b/templates/admin/library/folder.mako @@ -0,0 +1,158 @@ +<%inherit file="/base.mako"/> + +<%def name="render_component( component )"> + <% + if isinstance( component, trans.app.model.LibraryFolder ): + return render_folder( component ) + elif isinstance( component, trans.app.model.LibraryFolderDatasetAssociation ): + return render_dataset( component ) + %> + +## Render the dataset `data` as history item, using `hid` as the displayed id +<%def name="render_dataset( data )"> + <% + if data.state in ['no state','',None]: + data_state = "queued" + else: + data_state = data.state + %> +

+
${data.display_name()}
+
+
+ ## Header row for history items (name, state, action buttons) +
+ %if data_state != 'ok': +
+ %endif +
+
+ edit attributes +
+ ##${data.display_name()} +
+ ## Body for history items, extra info and actions, data "peek" +
+ %if data_state == "queued": +
Job is waiting to run
+ %elif data_state == "running": +
Job is currently running
+ %elif data_state == "error": +
+ An error occurred running this job: ${data.display_info().strip()}, + report this error +
+ %elif data_state == "empty": +
No data: ${data.display_info()}
+ %elif data_state == "ok": +
+ ${data.blurb}, + format: ${data.ext}, + database: + %if data.dbkey == '?': + ${data.dbkey} + %else: + ${data.dbkey} + %endif +
+
Info: ${data.display_info()}
+ %if data.peek != "no peek": +
${data.display_peek()}
+ %endif + %else: +
Error: unknown dataset state "${data_state}".
+ %endif + ## Recurse for child datasets +
+
+
+ +## Render a folder +<%def name="render_folder( this_folder )"> +
+
Contents of Folder: ${this_folder.name}
+
+
+ <% + components = list( this_folder.folders ) + list( this_folder.datasets ) + components = [ ( getattr( components[i], "order_id" ), i, components [i] ) for i in xrange( len( components ) ) ] + components.sort() + components = [ tup[-1] for tup in components ] + %> + %for component in components: + ${render_component( component )} + %endfor +
+
+ +
+
+
+ +<%def name="title()">Manage Folder: ${folder.name} +
+
+ Libraries  |   + Groups  |   + Users +
+
Change Folder Attributes
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
Manage Folder Contents: ${folder.name}
+
+
+ %if folder.parent: + Up a Level + %elif folder.library_root: + Manage Library + %endif +
+
+
+ ${render_folder( folder )} +
+
+
+
diff --git a/templates/admin/library/libraries.mako b/templates/admin/library/libraries.mako new file mode 100644 index 00000000000..7e44442e1aa --- /dev/null +++ b/templates/admin/library/libraries.mako @@ -0,0 +1,19 @@ +<%inherit file="/base.mako"/> + +<%def name="title()">Libraries +
+
+ Groups  |   + Users +
+ +
+
Galaxy Libraries
+
+ %for library in libraries: + + %endfor +
+
diff --git a/templates/admin/library/library.mako b/templates/admin/library/library.mako new file mode 100644 index 00000000000..5d979af8abb --- /dev/null +++ b/templates/admin/library/library.mako @@ -0,0 +1,35 @@ +<%inherit file="/base.mako"/> + +<%def name="title()">Library +
+
+ Libraries  |   + Groups  |   + Users +
+
Manage Library '${library.name}'
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+
+
+ + +
 
+
+
+
+ Manage Root Folder +
diff --git a/templates/admin/library/new_dataset.mako b/templates/admin/library/new_dataset.mako new file mode 100644 index 00000000000..241e121ebeb --- /dev/null +++ b/templates/admin/library/new_dataset.mako @@ -0,0 +1,953 @@ +<%inherit file="/base.mako"/> + +<%def name="title()">Create New Library Dataset +
+
+ Libraries  |   + Groups  |   + Users +
+
Create a new Library Dataset
+
+
+ +
+ +
+
+
+
+ +
+
+ Here you may specify a list of URLs (one per line) or paste the contents of a file. +
+
+
+
+ +
Yes
+
+ Use this option if you are entering intervals by hand. +
+
+
+
+ +
+ +
+
+ Which format? See help below +
+
+
+
+ + ##this should be generated dynamically +
+
+
+
+
+ +
+
+
+
diff --git a/templates/admin/reload_tool.mako b/templates/admin/reload_tool.mako new file mode 100644 index 00000000000..1d050ea4764 --- /dev/null +++ b/templates/admin/reload_tool.mako @@ -0,0 +1,28 @@ +<%inherit file="/base.mako"/> + +
+

Reload a Tool

+ + %if msg: + + %endif + + + +

${msg}

+
+

+ Reload tool: + + +

+
+
+
diff --git a/templates/admin_main.mako b/templates/admin_main.mako deleted file mode 100644 index 02e39576685..00000000000 --- a/templates/admin_main.mako +++ /dev/null @@ -1,39 +0,0 @@ -<%inherit file="/base.mako"/> -<%def name="title()">Galaxy Administration - - - - - - - - - - - - -
-

Galaxy Administration

- %if msg: -

${msg}

- %endif -
-
-

Admin password:

-

- Reload tool: - - -

-
-
- Manage Libraries -
- diff --git a/templates/library/admin_list_libraries.mako b/templates/library/admin_list_libraries.mako deleted file mode 100644 index ffe82787da2..00000000000 --- a/templates/library/admin_list_libraries.mako +++ /dev/null @@ -1,16 +0,0 @@ -<%inherit file="/base.mako"/> -<%def name="title()">View Libraries - -
-
Manage Libraries
-
- %for library in libraries: - - %endfor - -
-
diff --git a/templates/library/libraries.mako b/templates/library/libraries.mako new file mode 100644 index 00000000000..247333f709c --- /dev/null +++ b/templates/library/libraries.mako @@ -0,0 +1,13 @@ +<%inherit file="/base.mako"/> + +<%def name="title()">View Libraries +
+
View Library
+
+ %for library in libraries: + + %endfor +
+
diff --git a/templates/library/user_view_library.mako b/templates/library/library.mako similarity index 58% rename from templates/library/user_view_library.mako rename to templates/library/library.mako index 3b948558f28..2706efcde25 100644 --- a/templates/library/user_view_library.mako +++ b/templates/library/library.mako @@ -1,58 +1,44 @@ <%inherit file="/base.mako"/> <%def name="render_component( component )"> - <% + <% if isinstance( component, trans.app.model.LibraryFolder ): - return render_folder( component ) + return render_folder( component ) elif isinstance( component, trans.app.model.LibraryFolderDatasetAssociation ): - return render_dataset( component ) - %> + return render_dataset( component ) + %> - - ## Render the dataset `data` as history item, using `hid` as the displayed id <%def name="render_dataset( data )"> -
+
${data.name} -
+
- ## Render a folder <%def name="render_folder( this_folder )"> -
- Folder: ${this_folder.name} - <% - components = list( this_folder.folders ) + list( this_folder.datasets ) - components = [ ( getattr( components[i], "order_id" ), i, components [i] ) for i in xrange( len( components ) ) ] - components.sort() - components = [ tup[-1] for tup in components ] - %> -
- %for component in components: - ${render_component( component )} - %endfor -
+ Folder: ${this_folder.name} + <% + components = list( this_folder.folders ) + list( this_folder.datasets ) + components = [ ( getattr( components[i], "order_id" ), i, components [i] ) for i in xrange( len( components ) ) ] + components.sort() + components = [ tup[-1] for tup in components ] + %> +
+ %for component in components: + ${render_component( component )} + %endfor +
- - - - - - <%def name="title()">View Library: ${library.name} - -
+
Import from Library: ${library.name}
${render_folder( library.root_folder )} -
-
- diff --git a/templates/library/manage_dataset.mako b/templates/library/manage_dataset.mako deleted file mode 100644 index f04615def51..00000000000 --- a/templates/library/manage_dataset.mako +++ /dev/null @@ -1,100 +0,0 @@ -<%inherit file="/base.mako"/> -<%def name="title()">Edit Dataset Attributes - - -<%def name="datatype( dataset, datatypes )"> - - - -
-
Edit Attributes
-
-
- -
- -
- -
-
-
-
- -
- -
-
-
- %for element in metadata: -
- -
- ${element.get_html()} -
-
-
- %endfor -
- -
-
-
- -
- -
-
- This will inspect the dataset and attempt to correct the above column values - if they are not accurate. -
-
-
-
- -

- - -

-
Change data type
-
-
- -
- -
- ${datatype( dataset, datatypes )} -
- -
- This will change the datatype of the existing dataset - but not modify its contents. Use this if Galaxy - has incorrectly guessed the type of your dataset. -
-
-
-
- -
-
-
-
- -manage containing folder -

diff --git a/templates/library/manage_folder.mako b/templates/library/manage_folder.mako deleted file mode 100644 index 19e12555625..00000000000 --- a/templates/library/manage_folder.mako +++ /dev/null @@ -1,191 +0,0 @@ -<%inherit file="/base.mako"/> - -<%def name="render_component( component )"> - <% - if isinstance( component, trans.app.model.LibraryFolder ): - return render_folder( component ) - elif isinstance( component, trans.app.model.LibraryFolderDatasetAssociation ): - return render_dataset( component ) - %> - - - -## Render the dataset `data` as history item, using `hid` as the displayed id -<%def name="render_dataset( data )"> - <% - if data.state in ['no state','',None]: - data_state = "queued" - else: - data_state = data.state - %> - ##

-
-
${data.display_name()}
-
-
- - ## Header row for history items (name, state, action buttons) - -
- %if data_state != 'ok': -
- %endif -
-
- ##display data - edit attributes - ##delete -
- ##${data.display_name()} -
- - ## Body for history items, extra info and actions, data "peek" - -
- %if data_state == "queued": -
Job is waiting to run
- %elif data_state == "running": -
Job is currently running
- %elif data_state == "error": -
- An error occurred running this job: ${data.display_info().strip()}, - report this error -
- %elif data_state == "empty": -
No data: ${data.display_info()}
- %elif data_state == "ok": -
- ${data.blurb}, - format: ${data.ext}, - database: - %if data.dbkey == '?': - ${data.dbkey} - %else: - ${data.dbkey} - %endif -
-
Info: ${data.display_info()}
- %if data.peek != "no peek": -
${data.display_peek()}
- %endif - %else: -
Error: unknown dataset state "${data_state}".
- %endif - - ## Recurse for child datasets - - -
-
-
- - -## Render a folder -<%def name="render_folder( this_folder )"> - -
-
Contents of Folder: ${this_folder.name}
-
-
-
- <% - components = list( this_folder.folders ) + list( this_folder.datasets ) - components = [ ( getattr( components[i], "order_id" ), i, components [i] ) for i in xrange( len( components ) ) ] - components.sort() - components = [ tup[-1] for tup in components ] - %> - %for component in components: - ${render_component( component )} - %endfor - -
-
-
- -
- -
-
- - - - - - -<%def name="title()">Manage Folder: ${folder.name} - -
-
Change Folder Attributes
-
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
-
- -
-
- -
-
- -
-
-
- - - -
- -
-
Manage Folder Contents: ${folder.name}
-
-
- %if folder.parent: - up a level - %elif folder.library_root: - manage library - %endif -
-
-
- ${render_folder( folder )} -
- -
- -
- -
-
-
- diff --git a/templates/library/manage_library.mako b/templates/library/manage_library.mako deleted file mode 100644 index 5d94dfb6b78..00000000000 --- a/templates/library/manage_library.mako +++ /dev/null @@ -1,46 +0,0 @@ -<%inherit file="/base.mako"/> -<%def name="title()">Manage Library - - -
-
Edit a Library: ${library.name}
-
-
- -
- - -
- -
- - -
- -
- - -
- -
- -
- - -
- -
- - -
- -
- -
- diff --git a/templates/library/new_dataset.mako b/templates/library/new_dataset.mako deleted file mode 100644 index 1c517cdfede..00000000000 --- a/templates/library/new_dataset.mako +++ /dev/null @@ -1,997 +0,0 @@ -<%inherit file="/base.mako"/> -<%def name="title()">Create New Library Dataset - -
-
Create a new Library Dataset
-
-
- - - -
- - -
- - -
- - -
- -
- - -
- -
- Here you may specify a list of URLs (one per line) or paste the contents of a file. -
- - - -
- -
- -
- - -
Yes
- -
- - Use this option if you are entering intervals by hand. -
- - -
- -
- -
- - -
- -
- Which format? See help below -
- - -
- -
- -
- - -##this should be generated dynamically -
- - - -
- -
- - -
- -
- - -
-
-
- diff --git a/templates/library/user_list_libraries.mako b/templates/library/user_list_libraries.mako deleted file mode 100644 index 23a2b3b9ad1..00000000000 --- a/templates/library/user_list_libraries.mako +++ /dev/null @@ -1,14 +0,0 @@ -<%inherit file="/base.mako"/> -<%def name="title()">View Libraries - -
-
View Library
-
- %for library in libraries: - - %endfor - -
-
diff --git a/templates/root/masthead.mako b/templates/root/masthead.mako index 58d8f1776db..097d7707acc 100644 --- a/templates/root/masthead.mako +++ b/templates/root/masthead.mako @@ -20,6 +20,9 @@ | wiki | screencasts | blog + %if admin_user == "true": + | admin + %endif     diff --git a/universe_wsgi.ini.sample b/universe_wsgi.ini.sample index 6dcdf061f94..58ca578eb15 100644 --- a/universe_wsgi.ini.sample +++ b/universe_wsgi.ini.sample @@ -77,8 +77,8 @@ use_lint = false # NEVER enable this on a public site (even test or QA) use_interactive = true -# Admin Password -admin_pass = galaxy +# Admin Users - this should be a comma-separated list of valid Galaxy users +#admin_users = user1@bx.psu.edu,user2@bx.psu.edu # path to sendmail sendmail_path = /usr/sbin/sendmail