class AdminRepositoryGrid(RepositoryGrid): class DeletedColumn(grids.BooleanColumn): def get_value(self, trans, grid, repository): if repository.deleted: return 'yes' return '' columns = [ RepositoryGrid.NameColumn( "Name", key="name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoryGrid.HeadsColumn("Heads"), RepositoryGrid.UserColumn( "Owner", model_class=model.User, link=(lambda item: dict(operation="repositories_by_user", id=item.id)), attach_popup=False, key="User.username"), RepositoryGrid.DeprecatedColumn("Deprecated", key="deprecated", attach_popup=False), # Columns that are valid for filtering but are not visible. DeletedColumn("Deleted", key="deleted", attach_popup=False) ] columns.append( grids.MulticolFilterColumn("Search repository name", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard")) operations = [operation for operation in RepositoryGrid.operations] operations.append( grids.GridOperation("Delete", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False)) operations.append( grids.GridOperation("Undelete", allow_multiple=False, condition=(lambda item: item.deleted), async_compatible=False)) def build_initial_query(self, trans, **kwd): return trans.sa_session.query(model.Repository) \ .join(model.User.table)
class ReviewedRepositoriesIOwnGrid(RepositoriesWithReviewsGrid): title = "Reviewed repositories I own" columns = [ RepositoriesWithReviewsGrid.NameColumn("Repository name", key="name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoriesWithReviewsGrid.WithReviewsRevisionColumn("Reviewed revisions"), RepositoriesWithReviewsGrid.WithoutReviewsRevisionColumn("Revisions for review"), RepositoriesWithReviewsGrid.ReviewersColumn("Reviewers", attach_popup=False), RepositoryGrid.DeprecatedColumn("Deprecated") ] columns.append(grids.MulticolFilterColumn("Search repository name", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation("Inspect repository revisions", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False) ] def build_initial_query(self, trans, **kwd): return trans.sa_session.query(model.Repository) \ .join((model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id)) \ .filter(model.Repository.table.c.user_id == trans.user.id) \ .join((model.User.table, model.User.table.c.id == model.RepositoryReview.table.c.user_id)) \ .outerjoin((model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id)) \ .outerjoin((model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id))
class RepositoriesWithoutReviewsGrid(RepositoriesWithReviewsGrid): # This grid filters out repositories that have been marked as either deprecated or deleted. title = "Repositories with no reviews" columns = [ RepositoriesWithReviewsGrid.NameColumn("Repository name", key="name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoriesWithReviewsGrid.DescriptionColumn("Synopsis", key="description", attach_popup=False), RepositoriesWithReviewsGrid.WithoutReviewsRevisionColumn("Revisions for review"), RepositoriesWithReviewsGrid.UserColumn("Owner", model_class=model.User, attach_popup=False, key="User.username") ] columns.append(grids.MulticolFilterColumn("Search repository name, description", cols_to_filter=[columns[0], columns[1]], key="free-text-search", visible=False, filterable="standard")) operations = [grids.GridOperation("Inspect repository revisions", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False)] def build_initial_query(self, trans, **kwd): return trans.sa_session.query(model.Repository) \ .filter(and_(model.Repository.table.c.deleted == false(), model.Repository.table.c.deprecated == false(), model.Repository.reviews == null())) \ .join(model.User.table)
class RepositoriesReadyForReviewGrid(RepositoriesWithoutReviewsGrid): # Repositories that are ready for human review are those that either: # 1) Have no tools # 2) Have tools that have been proven to be functionally correct within Galaxy. # This grid filters out repositories that have been marked as either deprecated or deleted. title = "Repositories ready for review" columns = [ RepositoriesWithoutReviewsGrid.NameColumn( "Repository name", key="name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoriesWithoutReviewsGrid.DescriptionColumn("Synopsis", key="description", attach_popup=False), RepositoriesWithoutReviewsGrid.WithoutReviewsRevisionColumn( "Revisions for review"), RepositoriesWithoutReviewsGrid.UserColumn("Owner", model_class=model.User, attach_popup=False, key="User.username") ] columns.append( grids.MulticolFilterColumn("Search repository name, description", cols_to_filter=[columns[0], columns[1]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation("Inspect repository revisions", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False) ] def build_initial_query(self, trans, **kwd): return trans.sa_session.query(model.Repository) \ .filter(and_(model.Repository.table.c.deleted == false(), model.Repository.table.c.deprecated == false(), model.Repository.reviews == null())) \ .join(model.RepositoryMetadata.table) \ .filter(and_(model.RepositoryMetadata.table.c.downloadable == true(), or_(model.RepositoryMetadata.table.c.includes_tools == false(), and_(model.RepositoryMetadata.table.c.includes_tools == true(), model.RepositoryMetadata.table.c.tools_functionally_correct == true())))) \ .join(model.User.table)
class AdminController(BaseUIController, Admin): user_list_grid = admin_grids.UserGrid() role_list_grid = admin_grids.RoleGrid() group_list_grid = admin_grids.GroupGrid() manage_category_grid = admin_grids.ManageCategoryGrid() repository_grid = admin_grids.AdminRepositoryGrid() repository_metadata_grid = admin_grids.RepositoryMetadataGrid() delete_operation = grids.GridOperation( "Delete", condition=(lambda item: not item.deleted), allow_multiple=True) undelete_operation = grids.GridOperation( "Undelete", condition=(lambda item: item.deleted and not item.purged), allow_multiple=True) purge_operation = grids.GridOperation( "Purge", condition=(lambda item: item.deleted and not item.purged), allow_multiple=True) @web.expose @web.require_admin def browse_repositories(self, trans, **kwd): # We add parameters to the keyword dict in this method in order to rename the param # with an "f-" prefix, simulating filtering by clicking a search link. We have # to take this approach because the "-" character is illegal in HTTP requests. if 'operation' in kwd: operation = kwd['operation'].lower() if operation == "view_or_manage_repository": return trans.response.send_redirect( web.url_for(controller='repository', action='browse_repositories', **kwd)) elif operation == "edit_repository": return trans.response.send_redirect( web.url_for(controller='repository', action='edit_repository', **kwd)) elif operation == "repositories_by_user": # Eliminate the current filters if any exist. for k, v in list(kwd.items()): if k.startswith('f-'): del kwd[k] if 'user_id' in kwd: user = suc.get_user(trans.app, kwd['user_id']) kwd['f-email'] = user.email del kwd['user_id'] else: # The received id is the repository id, so we need to get the id of the user # that uploaded the repository. repository_id = kwd.get('id', None) repository = repository_util.get_repository_in_tool_shed( trans.app, repository_id) kwd['f-email'] = repository.user.email elif operation == "repositories_by_category": # Eliminate the current filters if any exist. for k, v in list(kwd.items()): if k.startswith('f-'): del kwd[k] category_id = kwd.get('id', None) category = suc.get_category(trans.app, category_id) kwd['f-Category.name'] = category.name elif operation == "receive email alerts": if kwd['id']: kwd['caller'] = 'browse_repositories' return trans.response.send_redirect( web.url_for(controller='repository', action='set_email_alerts', **kwd)) else: del kwd['operation'] elif operation == 'delete': return self.delete_repository(trans, **kwd) elif operation == "undelete": return self.undelete_repository(trans, **kwd) # The changeset_revision_select_field in the RepositoryGrid performs a refresh_on_change # which sends in request parameters like changeset_revison_1, changeset_revision_2, etc. One # of the many select fields on the grid performed the refresh_on_change, so we loop through # all of the received values to see which value is not the repository tip. If we find it, we # know the refresh_on_change occurred, and we have the necessary repository id and change set # revision to pass on. for k, v in kwd.items(): changeset_revision_str = 'changeset_revision_' if k.startswith(changeset_revision_str): repository_id = trans.security.encode_id( int(k.lstrip(changeset_revision_str))) repository = repository_util.get_repository_in_tool_shed( trans.app, repository_id) if repository.tip() != v: return trans.response.send_redirect( web.url_for(controller='repository', action='browse_repositories', operation='view_or_manage_repository', id=trans.security.encode_id(repository.id), changeset_revision=v)) # Render the list view return self.repository_grid(trans, **kwd) @web.expose @web.require_admin def browse_repository_metadata(self, trans, **kwd): if 'operation' in kwd: operation = kwd['operation'].lower() if operation == "delete": return self.delete_repository_metadata(trans, **kwd) if operation == "view_or_manage_repository_revision": # The received id is a RepositoryMetadata object id, so we need to get the # associated Repository and redirect to view_or_manage_repository with the # changeset_revision. repository_metadata = metadata_util.get_repository_metadata_by_id( trans.app, kwd['id']) repository = repository_metadata.repository kwd['id'] = trans.security.encode_id(repository.id) kwd['changeset_revision'] = repository_metadata.changeset_revision kwd['operation'] = 'view_or_manage_repository' return trans.response.send_redirect( web.url_for(controller='repository', action='browse_repositories', **kwd)) return self.repository_metadata_grid(trans, **kwd) @web.expose @web.require_admin def create_category(self, trans, **kwd): message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') name = kwd.get('name', '').strip() description = kwd.get('description', '').strip() if kwd.get('create_category_button', False): if not name or not description: message = 'Enter a valid name and a description' status = 'error' elif suc.get_category_by_name(trans.app, name): message = 'A category with that name already exists' status = 'error' else: # Create the category category = trans.app.model.Category(name=name, description=description) trans.sa_session.add(category) trans.sa_session.flush() # Update the Tool Shed's repository registry. trans.app.repository_registry.add_category_entry(category) message = "Category '%s' has been created" % escape( category.name) status = 'done' trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=message, status=status)) return trans.fill_template( '/webapps/tool_shed/category/create_category.mako', name=name, description=description, message=message, status=status) @web.expose @web.require_admin def delete_repository(self, trans, **kwd): message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') id = kwd.get('id', None) if id: # Deleting multiple items is currently not allowed (allow_multiple=False), so there will only be 1 id. ids = util.listify(id) count = 0 deleted_repositories = "" for repository_id in ids: repository = repository_util.get_repository_in_tool_shed( trans.app, repository_id) if repository: if not repository.deleted: # Mark all installable repository_metadata records as not installable. for repository_metadata in repository.downloadable_revisions: repository_metadata.downloadable = False trans.sa_session.add(repository_metadata) # Mark the repository admin role as deleted. repository_admin_role = repository.admin_role if repository_admin_role is not None: repository_admin_role.deleted = True trans.sa_session.add(repository_admin_role) repository.deleted = True trans.sa_session.add(repository) trans.sa_session.flush() # Update the repository registry. trans.app.repository_registry.remove_entry(repository) count += 1 deleted_repositories += " %s " % repository.name if count: message = "Deleted %d %s: %s" % ( count, inflector.cond_plural( len(ids), "repository"), escape(deleted_repositories)) else: message = "All selected repositories were already marked deleted." else: message = "No repository ids received for deleting." status = 'error' trans.response.send_redirect( web.url_for(controller='admin', action='browse_repositories', message=util.sanitize_text(message), status=status)) @web.expose @web.require_admin def delete_repository_metadata(self, trans, **kwd): message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') id = kwd.get('id', None) if id: ids = util.listify(id) count = 0 for repository_metadata_id in ids: repository_metadata = metadata_util.get_repository_metadata_by_id( trans.app, repository_metadata_id) trans.sa_session.delete(repository_metadata) trans.sa_session.flush() count += 1 if count: message = "Deleted %d repository metadata %s" % ( count, inflector.cond_plural(len(ids), "record")) else: message = "No repository metadata ids received for deleting." status = 'error' trans.response.send_redirect( web.url_for(controller='admin', action='browse_repository_metadata', message=util.sanitize_text(message), status=status)) @web.expose @web.require_admin def edit_category(self, trans, **kwd): '''Handle requests to edit TS category name or description''' message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') id = kwd.get('id', None) if not id: message = "No category ids received for editing" trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=message, status='error')) category = suc.get_category(trans.app, id) original_category_name = str(category.name) original_category_description = str(category.description) if kwd.get('edit_category_button', False): flush_needed = False new_name = kwd.get('name', '').strip() new_description = kwd.get('description', '').strip() if original_category_name != new_name: if not new_name: message = 'Enter a valid name' status = 'error' elif original_category_name != new_name and suc.get_category_by_name( trans.app, new_name): message = 'A category with that name already exists' status = 'error' else: category.name = new_name flush_needed = True if original_category_description != new_description: category.description = new_description if not flush_needed: flush_needed = True if flush_needed: trans.sa_session.add(category) trans.sa_session.flush() if original_category_name != new_name: # Update the Tool Shed's repository registry. trans.app.repository_registry.edit_category_entry( original_category_name, new_name) message = "The information has been saved for category '%s'" % escape( category.name) status = 'done' return trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=message, status=status)) return trans.fill_template( '/webapps/tool_shed/category/edit_category.mako', category=category, message=message, status=status) @web.expose @web.require_admin def manage_categories(self, trans, **kwd): if 'f-free-text-search' in kwd: # Trick to enable searching repository name, description from the CategoryGrid. # What we've done is rendered the search box for the RepositoryGrid on the grid.mako # template for the CategoryGrid. See ~/templates/webapps/tool_shed/category/grid.mako. # Since we are searching repositories and not categories, redirect to browse_repositories(). return trans.response.send_redirect( web.url_for(controller='admin', action='browse_repositories', **kwd)) if 'operation' in kwd: operation = kwd['operation'].lower() if operation == "create": return trans.response.send_redirect( web.url_for(controller='admin', action='create_category', **kwd)) elif operation == "delete": return trans.response.send_redirect( web.url_for(controller='admin', action='mark_category_deleted', **kwd)) elif operation == "undelete": return trans.response.send_redirect( web.url_for(controller='admin', action='undelete_category', **kwd)) elif operation == "purge": return trans.response.send_redirect( web.url_for(controller='admin', action='purge_category', **kwd)) elif operation == "edit": return trans.response.send_redirect( web.url_for(controller='admin', action='edit_category', **kwd)) return self.manage_category_grid(trans, **kwd) @web.expose @web.require_admin def regenerate_statistics(self, trans, **kwd): message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') if 'regenerate_statistics_button' in kwd: trans.app.shed_counter.generate_statistics() message = "Successfully regenerated statistics" return trans.fill_template('/webapps/tool_shed/admin/statistics.mako', message=message, status=status) @web.expose @web.require_admin def manage_role_associations(self, trans, **kwd): """Manage users, groups and repositories associated with a role.""" role_id = kwd.get('id', None) role = repository_util.get_role_by_id(trans.app, role_id) # We currently only have a single role associated with a repository, the repository admin role. repository_role_association = role.repositories[0] repository = repository_role_association.repository associations_dict = repository_util.handle_role_associations( trans.app, role, repository, **kwd) in_users = associations_dict.get('in_users', []) out_users = associations_dict.get('out_users', []) in_groups = associations_dict.get('in_groups', []) out_groups = associations_dict.get('out_groups', []) message = associations_dict.get('message', '') status = associations_dict.get('status', 'done') return trans.fill_template('/webapps/tool_shed/role/role.mako', in_admin_controller=True, repository=repository, role=role, in_users=in_users, out_users=out_users, in_groups=in_groups, out_groups=out_groups, message=message, status=status) @web.expose @web.require_admin def reset_metadata_on_selected_repositories_in_tool_shed( self, trans, **kwd): rmm = repository_metadata_manager.RepositoryMetadataManager( trans.app, trans.user) if 'reset_metadata_on_selected_repositories_button' in kwd: message, status = rmm.reset_metadata_on_selected_repositories( **kwd) else: message = escape(util.restore_text(kwd.get('message', ''))) status = kwd.get('status', 'done') repositories_select_field = rmm.build_repository_ids_select_field( name='repository_ids', multiple=True, display='checkboxes', my_writable=False) return trans.fill_template( '/webapps/tool_shed/common/reset_metadata_on_selected_repositories.mako', repositories_select_field=repositories_select_field, message=message, status=status) @web.expose @web.require_admin def undelete_repository(self, trans, **kwd): message = escape(kwd.get('message', '')) id = kwd.get('id', None) if id: # Undeleting multiple items is currently not allowed (allow_multiple=False), so there will only be 1 id. ids = util.listify(id) count = 0 undeleted_repositories = "" for repository_id in ids: repository = repository_util.get_repository_in_tool_shed( trans.app, repository_id) if repository: if repository.deleted: # Inspect all repository_metadata records to determine those that are installable, and mark # them accordingly. for repository_metadata in repository.metadata_revisions: metadata = repository_metadata.metadata if metadata: if metadata_util.is_downloadable(metadata): repository_metadata.downloadable = True trans.sa_session.add(repository_metadata) # Mark the repository admin role as not deleted. repository_admin_role = repository.admin_role if repository_admin_role is not None: repository_admin_role.deleted = False trans.sa_session.add(repository_admin_role) repository.deleted = False trans.sa_session.add(repository) trans.sa_session.flush() if not repository.deprecated: # Update the repository registry. trans.app.repository_registry.add_entry(repository) count += 1 undeleted_repositories += " %s" % repository.name if count: message = "Undeleted %d %s: %s" % ( count, inflector.cond_plural( count, "repository"), undeleted_repositories) else: message = "No selected repositories were marked deleted, so they could not be undeleted." else: message = "No repository ids received for undeleting." trans.response.send_redirect( web.url_for(controller='admin', action='browse_repositories', message=util.sanitize_text(message), status='done')) @web.expose @web.require_admin def mark_category_deleted(self, trans, **kwd): # TODO: We should probably eliminate the Category.deleted column since it really makes no # sense to mark a category as deleted (category names and descriptions can be changed instead). # If we do this, and the following 2 methods can be eliminated. message = escape(kwd.get('message', '')) id = kwd.get('id', None) if id: ids = util.listify(id) message = "Deleted %d categories: " % len(ids) for category_id in ids: category = suc.get_category(trans.app, category_id) category.deleted = True trans.sa_session.add(category) trans.sa_session.flush() # Update the Tool Shed's repository registry. trans.app.repository_registry.remove_category_entry(category) message += " %s " % escape(category.name) else: message = "No category ids received for deleting." trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=util.sanitize_text(message), status='done')) @web.expose @web.require_admin def purge_category(self, trans, **kwd): # This method should only be called for a Category that has previously been deleted. # Purging a deleted Category deletes all of the following from the database: # - RepoitoryCategoryAssociations where category_id == Category.id message = escape(kwd.get('message', '')) id = kwd.get('id', None) if id: ids = util.listify(id) count = 0 purged_categories = "" message = "Purged %d categories: " % len(ids) for category_id in ids: category = suc.get_category(trans.app, category_id) if category.deleted: # Delete RepositoryCategoryAssociations for rca in category.repositories: trans.sa_session.delete(rca) trans.sa_session.flush() purged_categories += " %s " % category.name message = "Purged %d categories: %s" % (count, escape(purged_categories)) else: message = "No category ids received for purging." trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=util.sanitize_text(message), status='done')) @web.expose @web.require_admin def undelete_category(self, trans, **kwd): message = escape(kwd.get('message', '')) id = kwd.get('id', None) if id: ids = util.listify(id) count = 0 undeleted_categories = "" for category_id in ids: category = suc.get_category(trans.app, category_id) if category.deleted: category.deleted = False trans.sa_session.add(category) trans.sa_session.flush() # Update the Tool Shed's repository registry. trans.app.repository_registry.add_category_entry(category) count += 1 undeleted_categories += " %s" % category.name message = "Undeleted %d categories: %s" % ( count, escape(undeleted_categories)) else: message = "No category ids received for undeleting." trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=util.sanitize_text(message), status='done'))
class RepositoryMetadataGrid(grids.Grid): class IdColumn(grids.IntegerColumn): def get_value(self, trans, grid, repository_metadata): return repository_metadata.id class NameColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): return escape(repository_metadata.repository.name) class OwnerColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): return escape(repository_metadata.repository.user.username) class RevisionColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): repository = repository_metadata.repository return hg_util.get_revision_label(trans.app, repository, repository_metadata.changeset_revision, include_date=True, include_hash=True) class ToolsColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): tools_str = '0' if repository_metadata: metadata = repository_metadata.metadata if metadata: if 'tools' in metadata: # We used to display the following, but grid was too cluttered. # for tool_metadata_dict in metadata[ 'tools' ]: # tools_str += '%s <b>%s</b><br/>' % ( tool_metadata_dict[ 'id' ], tool_metadata_dict[ 'version' ] ) return '%d' % len(metadata['tools']) return tools_str class DatatypesColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): datatypes_str = '0' if repository_metadata: metadata = repository_metadata.metadata if metadata: if 'datatypes' in metadata: # We used to display the following, but grid was too cluttered. # for datatype_metadata_dict in metadata[ 'datatypes' ]: # datatypes_str += '%s<br/>' % datatype_metadata_dict[ 'extension' ] return '%d' % len(metadata['datatypes']) return datatypes_str class WorkflowsColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): workflows_str = '0' if repository_metadata: metadata = repository_metadata.metadata if metadata: if 'workflows' in metadata: # We used to display the following, but grid was too cluttered. # workflows_str += '<b>Workflows:</b><br/>' # metadata[ 'workflows' ] is a list of tuples where each contained tuple is # [ <relative path to the .ga file in the repository>, <exported workflow dict> ] # workflow_tups = metadata[ 'workflows' ] # workflow_metadata_dicts = [ workflow_tup[1] for workflow_tup in workflow_tups ] # for workflow_metadata_dict in workflow_metadata_dicts: # workflows_str += '%s<br/>' % workflow_metadata_dict[ 'name' ] return '%d' % len(metadata['workflows']) return workflows_str class DeletedColumn(grids.BooleanColumn): def get_value(self, trans, grid, repository_metadata): if repository_metadata.repository.deleted: return 'yes' return '' class DeprecatedColumn(grids.BooleanColumn): def get_value(self, trans, grid, repository_metadata): if repository_metadata.repository.deprecated: return 'yes' return '' class MaliciousColumn(grids.BooleanColumn): def get_value(self, trans, grid, repository_metadata): if repository_metadata.malicious: return 'yes' return '' # Grid definition title = "Repository Metadata" model_class = model.RepositoryMetadata default_sort_key = "name" use_hide_message = False columns = [ IdColumn("Id", visible=False, attach_popup=False), NameColumn("Name", key="name", model_class=model.Repository, link=(lambda item: dict(operation="view_or_manage_repository_revision", id=item.id)), attach_popup=True), OwnerColumn("Owner", attach_popup=False), RevisionColumn("Revision", attach_popup=False), ToolsColumn("Tools", attach_popup=False), DatatypesColumn("Datatypes", attach_popup=False), WorkflowsColumn("Workflows", attach_popup=False), DeletedColumn("Deleted", attach_popup=False), DeprecatedColumn("Deprecated", attach_popup=False), MaliciousColumn("Malicious", attach_popup=False) ] columns.append(grids.MulticolFilterColumn("Search repository name", cols_to_filter=[columns[1]], key="free-text-search", visible=False, filterable="standard")) operations = [grids.GridOperation("Delete", allow_multiple=False, allow_popup=True, async_compatible=False, confirm="Repository metadata records cannot be recovered after they are deleted. Click OK to delete the selected items.")] def build_initial_query(self, trans, **kwd): return trans.sa_session.query(model.RepositoryMetadata) \ .join(model.Repository.table)
class GroupGrid(grids.Grid): class NameColumn(grids.TextColumn): def get_value(self, trans, grid, group): return str(group.name) class StatusColumn(grids.GridColumn): def get_value(self, trans, grid, group): if group.deleted: return "deleted" return "" class RolesColumn(grids.GridColumn): def get_value(self, trans, grid, group): if group.roles: return len(group.roles) return 0 class UsersColumn(grids.GridColumn): def get_value(self, trans, grid, group): if group.members: return len(group.members) return 0 title = "Groups" model_class = model.Group default_sort_key = "name" columns = [ NameColumn("Name", key="name", link=(lambda item: dict(operation="Manage users and roles", id=item.id)), attach_popup=True), UsersColumn("Users", attach_popup=False), RolesColumn("Roles", attach_popup=False), StatusColumn("Status", attach_popup=False), # Columns that are valid for filtering but are not visible. grids.DeletedColumn("Deleted", key="deleted", visible=False, filterable="advanced") ] columns.append(grids.MulticolFilterColumn("Search", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard")) global_actions = [ grids.GridAction("Add new group", dict(controller='admin', action='groups', operation='create')) ] operations = [grids.GridOperation("Rename", condition=(lambda item: not item.deleted), allow_multiple=False, url_args=dict(action="rename_group")), grids.GridOperation("Delete", condition=(lambda item: not item.deleted), allow_multiple=True, url_args=dict(action="mark_group_deleted")), grids.GridOperation("Undelete", condition=(lambda item: item.deleted), allow_multiple=True, url_args=dict(action="undelete_group")), grids.GridOperation("Purge", condition=(lambda item: item.deleted), allow_multiple=True, url_args=dict(action="purge_group"))] standard_filters = [ grids.GridColumnFilter("Active", args=dict(deleted=False)), grids.GridColumnFilter("Deleted", args=dict(deleted=True)), grids.GridColumnFilter("All", args=dict(deleted='All')) ] use_paging = False
class UserGrid(grids.Grid): class UserLoginColumn(grids.TextColumn): def get_value(self, trans, grid, user): return escape(user.email) class UserNameColumn(grids.TextColumn): def get_value(self, trans, grid, user): if user.username: return escape(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' class StatusColumn(grids.GridColumn): def get_value(self, trans, grid, user): if user.purged: return "purged" elif user.deleted: return "deleted" return "" class EmailColumn(grids.GridColumn): def filter(self, trans, user, query, column_filter): if column_filter == 'All': return query return query.filter(and_(model.Tool.table.c.user_id == model.User.table.c.id, model.User.table.c.email == column_filter)) title = "Users" model_class = model.User default_sort_key = "email" columns = [ UserLoginColumn("Email", key="email", link=(lambda item: dict(operation="information", id=item.id)), attach_popup=True, filterable="advanced"), UserNameColumn("User Name", key="username", attach_popup=False, filterable="advanced"), GroupsColumn("Groups", attach_popup=False), RolesColumn("Roles", attach_popup=False), ExternalColumn("External", attach_popup=False), LastLoginColumn("Last Login", format=time_ago), StatusColumn("Status", attach_popup=False), # Columns that are valid for filtering but are not visible. EmailColumn("Email", key="email", visible=False) ] columns.append(grids.MulticolFilterColumn("Search", cols_to_filter=[columns[0], columns[1]], key="free-text-search", visible=False, filterable="standard")) global_actions = [ grids.GridAction("Create new user", dict(controller='admin', action='users', operation='create')) ] operations = [ grids.GridOperation("Manage Roles and Groups", condition=(lambda item: not item.deleted), allow_multiple=False, url_args=dict(action="manage_roles_and_groups_for_user")), grids.GridOperation("Reset Password", condition=(lambda item: not item.deleted), allow_multiple=True, allow_popup=False, url_args=dict(action="reset_user_password")) ] standard_filters = [ grids.GridColumnFilter("Active", args=dict(deleted=False)), grids.GridColumnFilter("Deleted", args=dict(deleted=True, purged=False)), grids.GridColumnFilter("Purged", args=dict(purged=True)), grids.GridColumnFilter("All", args=dict(deleted='All')) ] use_paging = False def get_current_item(self, trans, **kwargs): return trans.user
class RoleGrid(grids.Grid): class NameColumn(grids.TextColumn): def get_value(self, trans, grid, role): return escape(str(role.name)) class DescriptionColumn(grids.TextColumn): def get_value(self, trans, grid, role): if role.description: return str(role.description) return '' class TypeColumn(grids.TextColumn): def get_value(self, trans, grid, role): return str(role.type) class StatusColumn(grids.GridColumn): def get_value(self, trans, grid, role): if role.deleted: return "deleted" return "" class GroupsColumn(grids.GridColumn): def get_value(self, trans, grid, role): if role.groups: return len(role.groups) return 0 class RepositoriesColumn(grids.GridColumn): def get_value(self, trans, grid, role): if role.repositories: return len(role.repositories) return 0 class UsersColumn(grids.GridColumn): def get_value(self, trans, grid, role): if role.users: return len(role.users) return 0 title = "Roles" model_class = model.Role default_sort_key = "name" columns = [ NameColumn("Name", key="name", link=(lambda item: dict(operation="Manage role associations", id=item.id)), attach_popup=True, filterable="advanced"), DescriptionColumn("Description", key='description', attach_popup=False, filterable="advanced"), GroupsColumn("Groups", attach_popup=False), RepositoriesColumn("Repositories", attach_popup=False), UsersColumn("Users", attach_popup=False), # Columns that are valid for filtering but are not visible. grids.DeletedColumn("Deleted", key="deleted", visible=False, filterable="advanced") ] columns.append(grids.MulticolFilterColumn("Search", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard")) global_actions = [ grids.GridAction("Add new role", dict(controller='admin', action='roles', operation='create')) ] # Repository admin roles currently do not have any operations since they are managed automatically based # on other events. For example, if a repository is renamed, its associated admin role is automatically # renamed accordingly and if a repository is deleted its associated admin role is automatically deleted. operations = [grids.GridOperation("Rename", condition=(lambda item: not item.deleted and not item.is_repository_admin_role), allow_multiple=False, url_args=dict(action="rename_role")), grids.GridOperation("Delete", condition=(lambda item: not item.deleted and not item.is_repository_admin_role), allow_multiple=True, url_args=dict(action="mark_role_deleted")), grids.GridOperation("Undelete", condition=(lambda item: item.deleted and not item.is_repository_admin_role), allow_multiple=True, url_args=dict(action="undelete_role")), grids.GridOperation("Purge", condition=(lambda item: item.deleted and not item.is_repository_admin_role), allow_multiple=True, url_args=dict(action="purge_role"))] standard_filters = [ grids.GridColumnFilter("Active", args=dict(deleted=False)), grids.GridColumnFilter("Deleted", args=dict(deleted=True)), grids.GridColumnFilter("All", args=dict(deleted='All')) ] use_paging = False def apply_query_filter(self, trans, query, **kwd): return query.filter(model.Role.type != model.Role.types.PRIVATE)
class RepositoriesWithReviewsGrid(RepositoryGrid): # This grid filters out repositories that have been marked as either deprecated or deleted. class WithReviewsRevisionColumn(grids.GridColumn): def get_value(self, trans, grid, repository): # Restrict to revisions that have been reviewed. if repository.reviews: rval = '' repo = repository.hg_repo for review in repository.reviews: changeset_revision = review.changeset_revision rev, label = hg_util.get_rev_label_from_changeset_revision( repo, changeset_revision) rval += '<a href="manage_repository_reviews_of_revision?id=%s&changeset_revision=%s">%s</a><br/>' % \ (trans.security.encode_id(repository.id), changeset_revision, label) return rval return '' class WithoutReviewsRevisionColumn(grids.GridColumn): def get_value(self, trans, grid, repository): # Restrict the options to revisions that have not yet been reviewed. repository_metadata_revisions = metadata_util.get_repository_metadata_revisions_for_review( repository, reviewed=False) if repository_metadata_revisions: rval = '' for repository_metadata in repository_metadata_revisions: rev, label, changeset_revision = \ hg_util.get_rev_label_changeset_revision_from_repository_metadata(trans.app, repository_metadata, repository=repository, include_date=True, include_hash=False) rval += '<a href="manage_repository_reviews_of_revision?id=%s&changeset_revision=%s">%s</a><br/>' % \ (trans.security.encode_id(repository.id), changeset_revision, label) return rval return '' class ReviewersColumn(grids.TextColumn): def get_value(self, trans, grid, repository): rval = '' if repository.reviewers: for user in repository.reviewers: rval += '<a class="view-info" href="repository_reviews_by_user?id=%s">' % trans.security.encode_id( user.id) rval += '%s</a> | ' % user.username if rval[-3:] == ' | ': rval = rval[:-3] return rval class RatingColumn(grids.TextColumn): def get_value(self, trans, grid, repository): rval = '' for review in repository.reviews: if review.rating: for index in range(1, 6): rval += '<input ' rval += 'name="star1-%s" ' % trans.security.encode_id( review.id) rval += 'type="radio" ' rval += 'class="community_rating_star star" ' rval += 'disabled="disabled" ' rval += 'value="%s" ' % str(review.rating) if review.rating > (index - 0.5) and review.rating < ( index + 0.5): rval += 'checked="checked" ' rval += '/>' rval += '<br/>' return rval class ApprovedColumn(grids.TextColumn): def get_value(self, trans, grid, repository): rval = '' for review in repository.reviews: if review.approved: rval += '%s<br/>' % review.approved return rval title = "All reviewed repositories" model_class = model.Repository template = '/webapps/tool_shed/repository_review/grid.mako' default_sort_key = "Repository.name" columns = [ RepositoryGrid.NameColumn( "Repository name", key="name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoryGrid.UserColumn("Owner", model_class=model.User, attach_popup=False, key="User.username"), WithReviewsRevisionColumn("Reviewed revisions"), ReviewersColumn("Reviewers", attach_popup=False), RatingColumn("Rating", attach_popup=False), ApprovedColumn("Approved", attach_popup=False) ] columns.append( grids.MulticolFilterColumn("Search repository name", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation("Inspect repository revisions", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False) ] def build_initial_query(self, trans, **kwd): return trans.sa_session.query(model.Repository) \ .filter(and_(model.Repository.table.c.deleted == false(), model.Repository.table.c.deprecated == false())) \ .join((model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id)) \ .join((model.User.table, model.User.table.c.id == model.Repository.table.c.user_id)) \ .outerjoin((model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id)) \ .outerjoin((model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id))
class RepositoryReviewsByUserGrid(grids.Grid): # This grid filters out repositories that have been marked as deprecated. class RepositoryNameColumn(grids.TextColumn): def get_value(self, trans, grid, review): return escape(review.repository.name) class RepositoryDescriptionColumn(grids.TextColumn): def get_value(self, trans, grid, review): return escape(review.repository.description) class RevisionColumn(grids.TextColumn): def get_value(self, trans, grid, review): encoded_review_id = trans.security.encode_id(review.id) rval = '<a class="action-button" href="' if review.user == trans.user: rval += 'edit_review' else: rval += 'browse_review' revision_label = hg_util.get_revision_label( trans.app, review.repository, review.changeset_revision, include_date=True, include_hash=False) rval += f'?id={encoded_review_id}">{revision_label}</a>' return rval class RatingColumn(grids.TextColumn): def get_value(self, trans, grid, review): if review.rating: for index in range(1, 6): rval = '<input ' rval += 'name="star1-%s" ' % trans.security.encode_id( review.id) rval += 'type="radio" ' rval += 'class="community_rating_star star" ' rval += 'disabled="disabled" ' rval += 'value="%s" ' % str(review.rating) if review.rating > (index - 0.5) and review.rating < (index + 0.5): rval += 'checked="checked" ' rval += '/>' return rval return '' title = "Reviews by user" model_class = model.RepositoryReview template = '/webapps/tool_shed/repository_review/grid.mako' default_sort_key = 'repository_id' use_hide_message = False columns = [ RepositoryNameColumn( "Repository Name", model_class=model.Repository, key="Repository.name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoryDescriptionColumn("Description", model_class=model.Repository, key="Repository.description", attach_popup=False), RevisionColumn("Revision", attach_popup=False), RatingColumn("Rating", attach_popup=False), ] operations = [ grids.GridOperation("Inspect repository revisions", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False) ] num_rows_per_page = 50 def build_initial_query(self, trans, **kwd): user_id = trans.security.decode_id(kwd['id']) return trans.sa_session.query(model.RepositoryReview) \ .filter(and_(model.RepositoryReview.table.c.deleted == false(), model.RepositoryReview.table.c.user_id == user_id)) \ .join((model.Repository.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id)) \ .filter(model.Repository.table.c.deprecated == false())