diff --git a/client/galaxy/scripts/mvc/groups/group-detail-view.js b/client/galaxy/scripts/mvc/groups/group-detail-view.js new file mode 100644 index 00000000000..062ae79274e --- /dev/null +++ b/client/galaxy/scripts/mvc/groups/group-detail-view.js @@ -0,0 +1,134 @@ +define([ + "libs/toastr", + "mvc/groups/group-model" + ], +function( + mod_toastr, + mod_group_model + ) { + +// toolshed group detail view +var GroupDetailView = Backbone.View.extend({ + el: "#groups_element", + options: {}, + app: null, + + initialize : function( options ){ + this.options = _.extend( this.options, options ); + this.app = window.globalTS.groups; + + if ( this.app.collection !== null ){ + this.model = this.app.collection.get( this.options.group_id ); + this.render(); + } else { + this.fetchGroup(); + } + + }, + + fetchGroup: function( options ){ + var that = this; + this.options = _.extend( this.options, options ); + this.model = new mod_group_model.Group( { id: this.options.group_id } ); + this.model.fetch({ + success: function( model ){ + console.log( 'received data: ' ); + console.log( model ); + that.render(); + }, + error: function( model, response ){ + if ( typeof response.responseJSON !== "undefined" ){ + mod_toastr.error( response.responseJSON.err_msg ); + } else { + mod_toastr.error( 'An error ocurred.' ); + } + } + }); + }, + + render: function(){ + var template = this.templateRow(); + this.$el.html( template( { group: this.model } ) ); + $("#center [data-toggle]").tooltip(); + }, + + templateRow: function() { + return _.template([ + '
', + '

<%= group.get("name") %>

', + '

', + 'A group of <%= group.get("members").length %> members with <%= group.get("repositories").length %> repositories and a total of <%= group.get("total_downloads") %> combined tool downloads.

', + + '

Members

', + '', + '', + '', + '', + '', + '', + '', + '<% _.each(group.get("members"), function(member) { %>', + '', + '', + '', + '', + '', + '<% }); %>', + '', + '
NameRepositoriesRegistered
', + '<%= member.username %>', + '', + '<%= member.user_repos_count %>', + '', + '<%= member.time_created %>', + '
', + + '

Repositories

', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '<% _.each(group.get("repositories"), function(repo) { %>', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '<% }); %>', + '', + '
NameDescriptionDownloadsOwnerCreatedRatingVerified
', + '<%= repo.name %>', + '', + '<%= repo.description %>', + '', + '<%= repo.times_downloaded %>', + '', + '<%= repo.owner %>', + '', + '<%= repo.time_created %>', + '', + '<%= repo.ratings_mean %>', + '', + '<%= repo.approved %>', + '
', + '
' + ].join('')); + } +}); +return { + GroupDetailView: GroupDetailView +}; + +}); + diff --git a/client/galaxy/scripts/mvc/groups/group-list-view.js b/client/galaxy/scripts/mvc/groups/group-list-view.js new file mode 100644 index 00000000000..3adeb0feefb --- /dev/null +++ b/client/galaxy/scripts/mvc/groups/group-list-view.js @@ -0,0 +1,148 @@ +define([ + "libs/toastr", + "mvc/groups/group-model", + "mvc/groups/group-listrow-view" + +], function( + mod_toastr, + mod_group_model, + mod_group_listrow_view +){ + +var GroupListView = Backbone.View.extend({ + el: '#groups_element', + defaults: {}, + + /** + * Initialize and fetch the groups from server. + * Async render afterwards. + * @param {object} options an object with options + */ + initialize : function( options ){ + this.options = _.defaults( this.options || {}, this.defaults, options ); + var that = this; + window.globalTS.groups.collection = new mod_group_model.Groups(); + window.globalTS.groups.collection.fetch({ + success: function( model ){ + console.log( 'received data: ' ); + console.log( model ); + that.render(); + }, + error: function( model, response ){ + if ( typeof response.responseJSON !== "undefined" ){ + mod_toastr.error( response.responseJSON.err_msg ); + } else { + mod_toastr.error( 'An error ocurred.' ); + } + } + }); + + }, + + fetch: function(){ + }, + + /** + * Render the groups table from the object's own collection. + */ + render: function ( options ) { + this.options = _.extend( this.options, options ); + $( ".tooltip" ).hide(); + var template = this.templateGroupsList(); + this.$el.html( template( { length: 10, order: 'asc' } ) ); + this.renderRows( window.globalTS.groups.collection.models ); + $( "#center [data-toggle]" ).tooltip(); + $( "#center" ).css( 'overflow','auto' ); + }, + + /** + * Render all given models as rows in the groups list + * @param {array} groups_to_render array of group models to render + */ + renderRows: function( groups_to_render ){ + for ( var i = 0; i < groups_to_render.length; i++ ) { + var group = groups_to_render[i]; + this.renderOne( { group: group } ) + } + }, + + /** + * Create a view for the given model and add it to the groups view. + * @param {Group} model of the view that will be rendered + */ + renderOne: function( options ){ + var rowView = new mod_group_listrow_view.GroupListRowView( options ); + this.$el.find( '#group_list_body' ).append( rowView.el ); + }, + + /** + * Table heading was clicked, update sorting preferences and re-render. + * @return {[type]} [description] + */ + // sort_clicked : function(){ + // if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){ + // Galaxy.libraries.preferences.set({'sort_order': 'desc'}); + // } else { + // Galaxy.libraries.preferences.set({'sort_order': 'asc'}); + // } + // this.render(); + // }, + + /** + * Sort the underlying collection according to the parameters received. + * Currently supports only sorting by name. + */ + // sortLibraries: function(){ + // if (Galaxy.libraries.preferences.get('sort_by') === 'name'){ + // if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){ + // this.collection.sortByNameAsc(); + // } else if (Galaxy.libraries.preferences.get('sort_order') === 'desc'){ + // this.collection.sortByNameDesc(); + // } + // } + // }, + + // redirectToHome: function(){ + // window.location = '../'; + // }, + // redirectToLogin: function(){ + // window.location = '/user/login'; + // }, + +// MMMMMMMMMMMMMMMMMM +// === TEMPLATES ==== +// MMMMMMMMMMMMMMMMMM + + templateGroupsList: function(){ + tmpl_array = []; + + tmpl_array.push('
'); + tmpl_array.push('
'); + tmpl_array.push('
'); + tmpl_array.push('<% if(length === 0) { %>'); + tmpl_array.push('
There are no groups yet.
'); + tmpl_array.push('<% } else{ %>'); + tmpl_array.push(''); + tmpl_array.push(' '); + tmpl_array.push(' '); + tmpl_array.push(' '); + tmpl_array.push(' '); + tmpl_array.push(' '); + tmpl_array.push(' '); + tmpl_array.push(' '); + // group item views will attach here + tmpl_array.push(' '); + tmpl_array.push('
name descriptionmembersrepositories
'); + tmpl_array.push('<% }%>'); + tmpl_array.push('
'); + + return _.template(tmpl_array.join('')); + }, + +}); + +return { + GroupListView: GroupListView +}; + +}); diff --git a/client/galaxy/scripts/mvc/groups/group-listrow-view.js b/client/galaxy/scripts/mvc/groups/group-listrow-view.js new file mode 100644 index 00000000000..47421bbab79 --- /dev/null +++ b/client/galaxy/scripts/mvc/groups/group-listrow-view.js @@ -0,0 +1,36 @@ +define([], +function() { + +// toolshed group row view +var GroupListRowView = Backbone.View.extend({ + events: {}, + + initialize : function( options ){ + this.render( options.group ); + }, + + render: function( group ){ + var tmpl = this.templateRow(); + this.setElement(tmpl( { group:group } )); + this.$el.show(); + return this; + }, + + templateRow: function() { + return _.template([ + '">', + '"><%= group.get("name") %>', + 'description', + '<%= group.get("total_members") %>', + '<%= group.get("total_repos") %>', + '' + ].join('')); + } + +}); + +return { + GroupListRowView: GroupListRowView +}; + +}); diff --git a/client/galaxy/scripts/mvc/groups/group-model.js b/client/galaxy/scripts/mvc/groups/group-model.js new file mode 100644 index 00000000000..e9f810bbbab --- /dev/null +++ b/client/galaxy/scripts/mvc/groups/group-model.js @@ -0,0 +1,22 @@ +define([], function() { + +// ============================================================================ +// LIBRARY RELATED MODELS + + var Group = Backbone.Model.extend({ + urlRoot: '/api/groups', + }); + + var Groups = Backbone.Collection.extend({ + url: '/api/groups', + + model: Group, + + }); + +return { + Group: Group, + Groups: Groups +}; + +}); diff --git a/client/galaxy/scripts/toolshed.groups.js b/client/galaxy/scripts/toolshed.groups.js new file mode 100644 index 00000000000..1785878ed51 --- /dev/null +++ b/client/galaxy/scripts/toolshed.groups.js @@ -0,0 +1,51 @@ +// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +// === MAIN TOOLSHED GROUP MODULE ==== +// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM + +define([ + "mvc/groups/group-list-view", + "mvc/groups/group-detail-view", + "mvc/groups/group-model" + ], + function( + mod_group_list, + mod_group_detail, + mod_group_model + ) { + +// ============================================================================ +// ROUTER +var ToolshedRouter = Backbone.Router.extend({ + routes: { + "" : "groups", + ":group_id" : "group_page" + } + +}); + +var ToolshedGroups = Backbone.View.extend({ + groupListView: null, + groupDetailView: null, + collection: null, + + initialize : function(){ + window.globalTS.groups = this; + + this.ts_router = new ToolshedRouter(); + this.ts_router.on( 'route:groups', function() { + window.globalTS.groups.groupListView = new mod_group_list.GroupListView(); + }); + + this.ts_router.on( 'route:group_page', function( group_id ) { + window.globalTS.groups.groupDetailView = new mod_group_detail.GroupDetailView( { group_id: group_id } ); + }); + + Backbone.history.start({pushState: false}); + } +}); + +return { + ToolshedGroups: ToolshedGroups +}; + +}); diff --git a/lib/galaxy/web/framework/webapp.py b/lib/galaxy/web/framework/webapp.py index 49f350590bc..e813c29672a 100644 --- a/lib/galaxy/web/framework/webapp.py +++ b/lib/galaxy/web/framework/webapp.py @@ -818,8 +818,7 @@ class GalaxyWebTransaction( base.DefaultWebTransaction, template = template_lookup.get_template( filename ) template.output_encoding = 'utf-8' - data = dict( caller=self, t=self, trans=self, h=helpers, util=util, - request=self.request, response=self.response, app=self.app ) + data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app ) data.update( self.template_context ) data.update( kwargs ) return template.render( **data ) diff --git a/lib/galaxy/webapps/tool_shed/api/groups.py b/lib/galaxy/webapps/tool_shed/api/groups.py index 8ec4bc1f316..16ea547fb7e 100644 --- a/lib/galaxy/webapps/tool_shed/api/groups.py +++ b/lib/galaxy/webapps/tool_shed/api/groups.py @@ -3,8 +3,10 @@ import os from galaxy import util from galaxy import web +from galaxy.util import pretty_print_time_interval from galaxy.exceptions import RequestParameterMissingException from galaxy.exceptions import AdminRequiredException +from galaxy.exceptions import ObjectNotFound from galaxy.web import require_admin as require_admin from galaxy.web import _future_expose_api as expose_api from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous @@ -37,29 +39,12 @@ class GroupsController( BaseAPIController ): Example: GET localhost:9009/api/groups """ - model = trans.app.model group_dicts = [] deleted = util.asbool( deleted ) if deleted and not trans.user_is_admin(): raise AdminRequiredException( 'Only administrators can query deleted groups.' ) - for group in self.group_manager.list( trans ): - group_dict = group.to_dict( view='collection', value_mapper=self.__get_value_mapper( trans ) ) - group_members = [] - for uga in group.users: - member = ( trans.sa_session.query( model.User ).filter( model.User.table.c.id == uga.user_id ).one() ) - member_repositories = [] - for repo in trans.sa_session.query( model.Repository ) \ - .filter( model.Repository.table.c.user_id == uga.user_id ) \ - .join( model.RepositoryMetadata.table ) \ - .join( model.User.table ) \ - .outerjoin( model.RepositoryCategoryAssociation.table ) \ - .outerjoin( model.Category.table ): - member_repositories.append( { 'name': repo.name, 'times_downloaded': repo.times_downloaded } ) - - member_dict = { 'username' : member.username, 'repositories': member_repositories } - group_members.append( member_dict ) - group_dict[ 'members' ] = group_members - group_dicts.append( group_dict ) + for group in self.group_manager.list( trans, deleted ): + group_dicts.append( self._populate( trans, group ) ) return group_dicts @expose_api @@ -83,8 +68,7 @@ class GroupsController( BaseAPIController ): description = payload.get( 'description', '' ) if not description: description = '' - # TODO check existence - # if suc.get_category_by_name( trans.app, name ): + # TODO check group existence # raise exceptions.Conflict( 'A category with that name already exists.' ) else: # Create the group @@ -98,7 +82,7 @@ class GroupsController( BaseAPIController ): return group_dict @expose_api_anonymous_and_sessionless - def show( self, trans, id, **kwd ): + def show( self, trans, encoded_id, **kwd ): """ GET /api/groups/{encoded_group_id} Return a dictionary of information about a group. @@ -107,12 +91,52 @@ class GroupsController( BaseAPIController ): Example: GET localhost:9009/api/groups/f9cad7b01a472135 """ - # TODO write get group utility function - group = suc.get_group( trans.app, id ) + decoded_id = trans.security.decode_id( encoded_id ) + group = self.group_manager.get( trans, decoded_id ) if group is None: - group_dict = dict( message = 'Unable to locate group record for id %s.' % ( str( id ) ), status = 'error' ) - return group_dict - group_dict = group.to_dict( view='element', value_mapper=self.__get_value_mapper( trans ) ) + raise ObjectNotFound( 'Unable to locate group record for id %s.' % ( str( encoded_id ) ) ) + return self._populate( trans, group ) + + def _populate( self, trans, group ): + """ + Turn the given group information from DB into a dict + and add other characteristics like members and repositories. + """ + model = trans.app.model + group_dict = group.to_dict( view='collection', value_mapper=self.__get_value_mapper( trans ) ) + group_members = [] + group_repos = [] + total_downloads = 0 + for uga in group.users: + user = trans.sa_session.query( model.User ).filter( model.User.table.c.id == uga.user_id ).one() + user_repos_count = 0 + for repo in trans.sa_session.query( model.Repository ) \ + .filter( model.Repository.table.c.user_id == uga.user_id ) \ + .join( model.RepositoryMetadata.table ) \ + .join( model.User.table ) \ + .outerjoin( model.RepositoryCategoryAssociation.table ) \ + .outerjoin( model.Category.table ): + time_repo_created = pretty_print_time_interval( repo.create_time, True ) + approved = '' + ratings = [] + for review in repo.reviews: + if review.rating: + ratings.append( review.rating ) + if review.approved == 'yes': + approved = 'yes' + # TODO add user ratings + ratings_mean = str( float( sum( ratings ) ) / len( ratings ) ) if len( ratings ) > 0 else '' + total_downloads += repo.times_downloaded + group_repos.append( { 'name': repo.name, 'times_downloaded': repo.times_downloaded, 'owner': repo.user.username, 'time_created': time_repo_created, 'description': repo.description, 'approved': approved, 'ratings_mean': ratings_mean, 'tools': 'unknown' } ) + user_repos_count += 1 + encoded_user_id = trans.app.security.encode_id( repo.user.id ) + user_repos_url = web.url_for( controller='repository', action='browse_repositories_by_user', user_id=encoded_user_id ) + time_created = pretty_print_time_interval( user.create_time, True ) + member_dict = { 'id': encoded_user_id, 'username': user.username, 'user_repos_url': user_repos_url, 'user_repos_count': user_repos_count, 'user_tools_count': 'unknown', 'time_created': time_created } + group_members.append( member_dict ) + group_dict[ 'members' ] = group_members + group_dict[ 'total_members' ] = len( group_members ) + group_dict[ 'repositories' ] = group_repos + group_dict[ 'total_repos' ] = len( group_repos ) + group_dict[ 'total_downloads' ] = total_downloads return group_dict - - diff --git a/lib/galaxy/webapps/tool_shed/buildapp.py b/lib/galaxy/webapps/tool_shed/buildapp.py index df92168e881..2ffc2f153e6 100644 --- a/lib/galaxy/webapps/tool_shed/buildapp.py +++ b/lib/galaxy/webapps/tool_shed/buildapp.py @@ -91,6 +91,11 @@ def app_factory( global_conf, **kwargs ): controller='groups', action='index', conditions=dict( method=[ "GET" ] ) ) + webapp.mapper.connect( 'group', + '/api/groups/:encoded_id', + controller='groups', + action='show', + conditions=dict( method=[ "GET" ] ) ) webapp.mapper.connect( 'repo_search', '/api/search/', controller='search', diff --git a/lib/galaxy/webapps/tool_shed/controllers/groups.py b/lib/galaxy/webapps/tool_shed/controllers/groups.py new file mode 100644 index 00000000000..1e1e80f1351 --- /dev/null +++ b/lib/galaxy/webapps/tool_shed/controllers/groups.py @@ -0,0 +1,22 @@ +import logging +from galaxy import model, util +from galaxy import web +from galaxy.model.orm import and_, not_, or_ +from galaxy.web.base.controller import BaseUIController +from galaxy.web.framework.helpers import escape, grids + +log = logging.getLogger( __name__ ) + + +class Group( BaseUIController ): + + @web.expose + def index( self, trans, **kwd ): + # define app configuration for generic mako template + app = { + 'jscript' : "toolshed.groups" + } + return trans.fill_template( '/webapps/tool_shed/group/index.mako', + config = { + 'title': 'Tool Shed Groups', + 'app': app } ) diff --git a/static/maps/mvc/groups/group-detail-view.js.map b/static/maps/mvc/groups/group-detail-view.js.map new file mode 100644 index 00000000000..ef653f0f771 --- /dev/null +++ b/static/maps/mvc/groups/group-detail-view.js.map @@ -0,0 +1 @@ +{"version":3,"file":"group-detail-view.js","sources":["../../../src/mvc/groups/group-detail-view.js"],"names":["define","mod_toastr","mod_group_model","GroupDetailView","Backbone","View","extend","el","options","app","initialize","this","_","window","globalTS","groups","collection","model","get","group_id","render","fetchGroup","that","Group","id","fetch","success","console","log","error","response","responseJSON","err_msg","template","templateRow","$el","html","group","$","tooltip","join"],"mappings":"AAAAA,QACI,cACA,0BAEJ,SACIC,EACAC,GAIJ,GAAIC,GAAkBC,SAASC,KAAKC,QAChCC,GAAI,kBACJC,WACAC,IAAK,KAELC,WAAa,SAAUF,GACnBG,KAAKH,QAAUI,EAAEN,OAAQK,KAAKH,QAASA,GACvCG,KAAKF,IAAMI,OAAOC,SAASC,OAEE,OAAxBJ,KAAKF,IAAIO,YACVL,KAAKM,MAAQN,KAAKF,IAAIO,WAAWE,IAAKP,KAAKH,QAAQW,UACnDR,KAAKS,UAELT,KAAKU,cAKbA,WAAY,SAAUb,GAClB,GAAIc,GAAOX,IACXA,MAAKH,QAAUI,EAAEN,OAAQK,KAAKH,QAASA,GACvCG,KAAKM,MAAQ,GAAIf,GAAgBqB,OAASC,GAAIb,KAAKH,QAAQW,WAC3DR,KAAKM,MAAMQ,OACTC,QAAS,SAAUT,GACjBU,QAAQC,IAAK,mBACbD,QAAQC,IAAKX,GACbK,EAAKF,UAEPS,MAAO,SAAUZ,EAAOa,GAElB7B,EAAW4B,MADyB,mBAA1BC,GAASC,aACDD,EAASC,aAAaC,QAEtB,yBAM9BZ,OAAQ,WACJ,GAAIa,GAAWtB,KAAKuB,aACpBvB,MAAKwB,IAAIC,KAAMH,GAAYI,MAAO1B,KAAKM,SACvCqB,EAAE,yBAAyBC,WAG/BL,YAAa,WACT,MAAOtB,GAAEqB,UACD,QACI,oCACA,mCACA,kMAEA,mBACA,6CACI,UACI,gBACA,wBACA,sBACJ,WACA,UACI,wDACA,OACI,OACI,yBACJ,QACA,OACI,gPACJ,QACA,OACI,6BACJ,QACJ,QACA,YACJ,WACJ,WAEA,wBACA,6CACI,UACI,gBACA,uBACA,qBACA,iBACA,mBACA,kBACA,oBACJ,WACA,UACI,2DACA,OACI,OACI,6KACJ,QACA,OACI,0BACJ,QACA,OACI,+BACJ,QACA,OACI,oBACJ,QACA,OACI,2BACJ,QACA,OACI,2BACJ,QACA,OACI,uBACJ,QACJ,QACA,YACJ,WACJ,WACJ,UACNO,KAAK,OAGf,QACIrC,gBAAiBA"} \ No newline at end of file diff --git a/static/maps/mvc/groups/group-groupdetail-view.js.map b/static/maps/mvc/groups/group-groupdetail-view.js.map new file mode 100644 index 00000000000..4977e80ac92 --- /dev/null +++ b/static/maps/mvc/groups/group-groupdetail-view.js.map @@ -0,0 +1 @@ +{"version":3,"file":"group-groupdetail-view.js","sources":["../../../src/mvc/groups/group-groupdetail-view.js"],"names":["define","GroupDetailView","Backbone","View","extend","events","initialize","group","this","render","tmpl","templateRow","setElement","$el","show","_","template","join"],"mappings":"AAAAA,UACA,WAGA,GAAIC,GAAkBC,SAASC,KAAKC,QAChCC,UAGAC,WAAa,SAAUC,GACnBC,KAAKC,OAAQF,IAGjBE,OAAQ,SAAUF,GACd,GAAIG,GAAOF,KAAKG,aAGhB,OAFAH,MAAKI,WAAWF,GAAQH,MAAMA,KAC9BC,KAAKK,IAAIC,OACFN,MAGXG,YAAa,WACT,MAAOI,GAAEC,UACD,8BACNC,KAAK,OAKf,QACIhB,gBAAiBA"} \ No newline at end of file diff --git a/static/maps/mvc/groups/group-grouprow-view.js.map b/static/maps/mvc/groups/group-grouprow-view.js.map new file mode 100644 index 00000000000..29804f64894 --- /dev/null +++ b/static/maps/mvc/groups/group-grouprow-view.js.map @@ -0,0 +1 @@ +{"version":3,"file":"group-grouprow-view.js","sources":["../../../src/mvc/groups/group-grouprow-view.js"],"names":["define","GroupRowView","Backbone","View","extend","events","initialize","group","this","render","tmpl","templateRow","setElement","$el","show","_","template","join"],"mappings":"AAAAA,UACA,WAGA,GAAIC,GAAeC,SAASC,KAAKC,QAC7BC,UAGAC,WAAa,SAAUC,GACnBC,KAAKC,OAAOF,IAGhBE,OAAQ,SAAUF,GACd,GAAIG,GAAOF,KAAKG,aAIhB,OAFAH,MAAKI,WAAWF,GAAQH,MAAMA,KAC9BC,KAAKK,IAAIC,OACFN,MAaXG,YAAa,WACT,MAAOI,GAAEC,UACD,iDACA,iFACA,uBACA,6CACA,2CACA,SACNC,KAAK,OAKf,QACIhB,aAAcA"} \ No newline at end of file diff --git a/static/maps/mvc/groups/group-list-view.js.map b/static/maps/mvc/groups/group-list-view.js.map new file mode 100644 index 00000000000..3fce756f66a --- /dev/null +++ b/static/maps/mvc/groups/group-list-view.js.map @@ -0,0 +1 @@ +{"version":3,"file":"group-list-view.js","sources":["../../../src/mvc/groups/group-list-view.js"],"names":["define","mod_toastr","mod_group_model","mod_group_listrow_view","GroupListView","Backbone","View","extend","el","defaults","initialize","options","this","_","that","window","globalTS","groups","collection","Groups","fetch","success","model","console","log","render","error","response","responseJSON","err_msg","$","hide","template","templateGroupsList","$el","html","length","order","renderRows","models","tooltip","css","groups_to_render","i","group","renderOne","rowView","GroupListRowView","find","append","tmpl_array","push","join"],"mappings":"AAAAA,QACI,cACA,yBACA,iCAED,SACCC,EACAC,EACAC,GAGJ,GAAIC,GAAgBC,SAASC,KAAKC,QAC9BC,GAAI,kBACJC,YAOAC,WAAa,SAAUC,GACnBC,KAAKD,QAAUE,EAAEJ,SAAUG,KAAKD,YAAeC,KAAKH,SAAUE,EAC9D,IAAIG,GAAOF,IACXG,QAAOC,SAASC,OAAOC,WAAa,GAAIhB,GAAgBiB,OACxDJ,OAAOC,SAASC,OAAOC,WAAWE,OAChCC,QAAS,SAAUC,GACjBC,QAAQC,IAAK,mBACbD,QAAQC,IAAKF,GACbR,EAAKW,UAEPC,MAAO,SAAUJ,EAAOK,GAElB1B,EAAWyB,MADyB,mBAA1BC,GAASC,aACDD,EAASC,aAAaC,QAEtB,yBAO9BT,MAAO,aAMPK,OAAQ,SAAWd,GACfC,KAAKD,QAAUE,EAAEN,OAAQK,KAAKD,QAASA,GACvCmB,EAAG,YAAaC,MAChB,IAAIC,GAAWpB,KAAKqB,oBACpBrB,MAAKsB,IAAIC,KAAMH,GAAYI,OAAQ,GAAIC,MAAO,SAC9CzB,KAAK0B,WAAYvB,OAAOC,SAASC,OAAOC,WAAWqB,QACnDT,EAAG,yBAA0BU,UAC7BV,EAAG,WAAYW,IAAK,WAAW,SAOnCH,WAAY,SAAUI,GAClB,IAAM,GAAIC,GAAI,EAAGA,EAAID,EAAiBN,OAAQO,IAAM,CAChD,GAAIC,GAAQF,EAAiBC,EAC7B/B,MAAKiC,WAAaD,MAAOA,MAQjCC,UAAW,SAAUlC,GACjB,GAAImC,GAAU,GAAI3C,GAAuB4C,iBAAkBpC,EAC3DC,MAAKsB,IAAIc,KAAM,oBAAqBC,OAAQH,EAAQtC,KAyCxDyB,mBAAoB,WAuBhB,MAtBAiB,eAEAA,WAAWC,KAAK,qBAChBD,WAAWC,KAAK,UAChBD,WAAWC,KAAK,mDAChBD,WAAWC,KAAK,4BAChBD,WAAWC,KAAK,uCAChBD,WAAWC,KAAK,iBAChBD,WAAWC,KAAK,8CAChBD,WAAWC,KAAK,cAChBD,WAAWC,KAAK,mMAChBD,WAAWC,KAAK,gDAChBD,WAAWC,KAAK,6CAChBD,WAAWC,KAAK,iDAChBD,WAAWC,KAAK,eAChBD,WAAWC,KAAK,mCAEhBD,WAAWC,KAAK,eAChBD,WAAWC,KAAK,YAChBD,WAAWC,KAAK,UAChBD,WAAWC,KAAK,UAETtC,EAAEmB,SAASkB,WAAWE,KAAK,OAK1C,QACIhD,cAAeA"} \ No newline at end of file diff --git a/static/maps/mvc/groups/group-listrow-view.js.map b/static/maps/mvc/groups/group-listrow-view.js.map new file mode 100644 index 00000000000..a3a50073eab --- /dev/null +++ b/static/maps/mvc/groups/group-listrow-view.js.map @@ -0,0 +1 @@ +{"version":3,"file":"group-listrow-view.js","sources":["../../../src/mvc/groups/group-listrow-view.js"],"names":["define","GroupListRowView","Backbone","View","extend","events","initialize","options","this","render","group","tmpl","templateRow","setElement","$el","show","_","template","join"],"mappings":"AAAAA,UACA,WAGA,GAAIC,GAAmBC,SAASC,KAAKC,QACjCC,UAEAC,WAAa,SAAUC,GACnBC,KAAKC,OAAQF,EAAQG,QAGzBD,OAAQ,SAAUC,GACd,GAAIC,GAAOH,KAAKI,aAGhB,OAFAJ,MAAKK,WAAWF,GAAQD,MAAMA,KAC9BF,KAAKM,IAAIC,OACFP,MAGXI,YAAa,WACT,MAAOI,GAAEC,UACD,iDACA,iFACA,uBACA,6CACA,2CACA,SACNC,KAAK,OAKf,QACIjB,iBAAkBA"} \ No newline at end of file diff --git a/static/maps/mvc/groups/group-model.js.map b/static/maps/mvc/groups/group-model.js.map new file mode 100644 index 00000000000..7cf16222049 --- /dev/null +++ b/static/maps/mvc/groups/group-model.js.map @@ -0,0 +1 @@ +{"version":3,"file":"group-model.js","sources":["../../../src/mvc/groups/group-model.js"],"names":["define","Group","Backbone","Model","extend","urlRoot","Groups","Collection","url","model"],"mappings":"AAAAA,UAAW,WAKP,GAAIC,GAAQC,SAASC,MAAMC,QACzBC,QAAS,gBAGPC,EAASJ,SAASK,WAAWH,QAC/BI,IAAK,cAELC,MAAOR,GAIb,QACIA,MAAOA,EACPK,OAAQA"} \ No newline at end of file diff --git a/static/maps/mvc/groups/group.model.js.map b/static/maps/mvc/groups/group.model.js.map new file mode 100644 index 00000000000..8b65022f69d --- /dev/null +++ b/static/maps/mvc/groups/group.model.js.map @@ -0,0 +1 @@ +{"version":3,"file":"group.model.js","sources":["../../../src/mvc/groups/group.model.js"],"names":["define","Group","Backbone","Model","extend","urlRoot","Groups","Collection","url","model"],"mappings":"AAAAA,UAAW,WAKP,GAAIC,GAAQC,SAASC,MAAMC,QACzBC,QAAS,iBAGPC,EAASJ,SAASK,WAAWH,QAC/BI,IAAK,cAELC,MAAOR,GAIb,QACIA,MAAOA,EACPK,OAAQA"} \ No newline at end of file diff --git a/static/maps/toolshed.groups.js.map b/static/maps/toolshed.groups.js.map new file mode 100644 index 00000000000..2ef66e19331 --- /dev/null +++ b/static/maps/toolshed.groups.js.map @@ -0,0 +1 @@ +{"version":3,"file":"toolshed.groups.js","sources":["../src/toolshed.groups.js"],"names":["define","mod_group_list","mod_group_detail","ToolshedRouter","Backbone","Router","extend","routes",":group_id","ToolshedGroups","View","groupListView","groupDetailView","collection","initialize","window","globalTS","groups","this","ts_router","on","GroupListView","group_id","GroupDetailView","history","start","pushState"],"mappings":"AAIAA,QACQ,6BACA,+BACA,0BAEJ,SACIC,EACAC,GAMR,GAAIC,GAAiBC,SAASC,OAAOC,QACjCC,QACI,GAA4B,SAC5BC,YAA4B,gBAKhCC,EAAiBL,SAASM,KAAKJ,QAC/BK,cAAe,KACfC,gBAAiB,KACjBC,WAAY,KAEZC,WAAa,WACTC,OAAOC,SAASC,OAASC,KAEzBA,KAAKC,UAAY,GAAIhB,GACrBe,KAAKC,UAAUC,GAAI,eAAgB,WAC/BL,OAAOC,SAASC,OAAON,cAAgB,GAAIV,GAAeoB,gBAG9DH,KAAKC,UAAUC,GAAI,mBAAoB,SAAUE,GAC7CP,OAAOC,SAASC,OAAOL,gBAAkB,GAAIV,GAAiBqB,iBAAmBD,SAAUA,MAG/FlB,SAASoB,QAAQC,OAAOC,WAAW,MAI3C,QACIjB,eAAgBA"} \ No newline at end of file diff --git a/static/scripts/mvc/groups/group-detail-view.js b/static/scripts/mvc/groups/group-detail-view.js new file mode 100644 index 00000000000..fcee488d131 --- /dev/null +++ b/static/scripts/mvc/groups/group-detail-view.js @@ -0,0 +1,2 @@ +define(["libs/toastr","mvc/groups/group-model"],function(a,b){var c=Backbone.View.extend({el:"#groups_element",options:{},app:null,initialize:function(a){this.options=_.extend(this.options,a),this.app=window.globalTS.groups,null!==this.app.collection?(this.model=this.app.collection.get(this.options.group_id),this.render()):this.fetchGroup()},fetchGroup:function(c){var d=this;this.options=_.extend(this.options,c),this.model=new b.Group({id:this.options.group_id}),this.model.fetch({success:function(a){console.log("received data: "),console.log(a),d.render()},error:function(b,c){a.error("undefined"!=typeof c.responseJSON?c.responseJSON.err_msg:"An error ocurred.")}})},render:function(){var a=this.templateRow();this.$el.html(a({group:this.model})),$("#center [data-toggle]").tooltip()},templateRow:function(){return _.template(["
",'

<%= group.get("name") %>

','

','A group of <%= group.get("members").length %> members with <%= group.get("repositories").length %> repositories and a total of <%= group.get("total_downloads") %> combined tool downloads.

',"

Members

",'',"","","","","","",'<% _.each(group.get("members"), function(member) { %>',"","","","","","<% }); %>","","
NameRepositoriesRegistered
","<%= member.username %>","",'<%= member.user_repos_count %>',"","<%= member.time_created %>","
","

Repositories

",'',"","","","","","","","","","",'<% _.each(group.get("repositories"), function(repo) { %>',"","","","","","","","","","<% }); %>","","
NameDescriptionDownloadsOwnerCreatedRatingVerified
",'<%= repo.name %>',"","<%= repo.description %>","","<%= repo.times_downloaded %>","","<%= repo.owner %>","","<%= repo.time_created %>","","<%= repo.ratings_mean %>","","<%= repo.approved %>","
","
"].join(""))}});return{GroupDetailView:c}}); +//# sourceMappingURL=../../../maps/mvc/groups/group-detail-view.js.map \ No newline at end of file diff --git a/static/scripts/mvc/groups/group-groupdetail-view.js b/static/scripts/mvc/groups/group-groupdetail-view.js new file mode 100644 index 00000000000..ac9d5f5c776 --- /dev/null +++ b/static/scripts/mvc/groups/group-groupdetail-view.js @@ -0,0 +1,2 @@ +define([],function(){var a=Backbone.View.extend({events:{},initialize:function(a){this.render(a)},render:function(a){var b=this.templateRow();return this.setElement(b({group:a})),this.$el.show(),this},templateRow:function(){return _.template(["
TEST GROUP PAGE
"].join(""))}});return{GroupDetailView:a}}); +//# sourceMappingURL=../../../maps/mvc/groups/group-groupdetail-view.js.map \ No newline at end of file diff --git a/static/scripts/mvc/groups/group-grouprow-view.js b/static/scripts/mvc/groups/group-grouprow-view.js new file mode 100644 index 00000000000..49178b680f1 --- /dev/null +++ b/static/scripts/mvc/groups/group-grouprow-view.js @@ -0,0 +1,2 @@ +define([],function(){var a=Backbone.View.extend({events:{},initialize:function(a){this.render(a)},render:function(a){var b=this.templateRow();return this.setElement(b({group:a})),this.$el.show(),this},templateRow:function(){return _.template(['">','"><%= group.get("name") %>',"description",'<%= group.get("total_members") %>','<%= group.get("total_repos") %>',""].join(""))}});return{GroupRowView:a}}); +//# sourceMappingURL=../../../maps/mvc/groups/group-grouprow-view.js.map \ No newline at end of file diff --git a/static/scripts/mvc/groups/group-list-view.js b/static/scripts/mvc/groups/group-list-view.js new file mode 100644 index 00000000000..d14a196bd3f --- /dev/null +++ b/static/scripts/mvc/groups/group-list-view.js @@ -0,0 +1,2 @@ +define(["libs/toastr","mvc/groups/group-model","mvc/groups/group-listrow-view"],function(a,b,c){var d=Backbone.View.extend({el:"#groups_element",defaults:{},initialize:function(c){this.options=_.defaults(this.options||{},this.defaults,c);var d=this;window.globalTS.groups.collection=new b.Groups,window.globalTS.groups.collection.fetch({success:function(a){console.log("received data: "),console.log(a),d.render()},error:function(b,c){a.error("undefined"!=typeof c.responseJSON?c.responseJSON.err_msg:"An error ocurred.")}})},fetch:function(){},render:function(a){this.options=_.extend(this.options,a),$(".tooltip").hide();var b=this.templateGroupsList();this.$el.html(b({length:10,order:"asc"})),this.renderRows(window.globalTS.groups.collection.models),$("#center [data-toggle]").tooltip(),$("#center").css("overflow","auto")},renderRows:function(a){for(var b=0;b'),tmpl_array.push(""),tmpl_array.push('
'),tmpl_array.push("<% if(length === 0) { %>"),tmpl_array.push("
There are no groups yet.
"),tmpl_array.push("<% } else{ %>"),tmpl_array.push(''),tmpl_array.push(" "),tmpl_array.push(' '),tmpl_array.push(' '),tmpl_array.push(' '),tmpl_array.push(' '),tmpl_array.push(" "),tmpl_array.push(' '),tmpl_array.push(" "),tmpl_array.push("
name descriptionmembersrepositories
"),tmpl_array.push("<% }%>"),tmpl_array.push("
"),_.template(tmpl_array.join(""))}});return{GroupListView:d}}); +//# sourceMappingURL=../../../maps/mvc/groups/group-list-view.js.map \ No newline at end of file diff --git a/static/scripts/mvc/groups/group-listrow-view.js b/static/scripts/mvc/groups/group-listrow-view.js new file mode 100644 index 00000000000..865816453d9 --- /dev/null +++ b/static/scripts/mvc/groups/group-listrow-view.js @@ -0,0 +1,2 @@ +define([],function(){var a=Backbone.View.extend({events:{},initialize:function(a){this.render(a.group)},render:function(a){var b=this.templateRow();return this.setElement(b({group:a})),this.$el.show(),this},templateRow:function(){return _.template(['">','"><%= group.get("name") %>',"description",'<%= group.get("total_members") %>','<%= group.get("total_repos") %>',""].join(""))}});return{GroupListRowView:a}}); +//# sourceMappingURL=../../../maps/mvc/groups/group-listrow-view.js.map \ No newline at end of file diff --git a/static/scripts/mvc/groups/group-model.js b/static/scripts/mvc/groups/group-model.js new file mode 100644 index 00000000000..ab732aa056b --- /dev/null +++ b/static/scripts/mvc/groups/group-model.js @@ -0,0 +1,2 @@ +define([],function(){var a=Backbone.Model.extend({urlRoot:"/api/groups"}),b=Backbone.Collection.extend({url:"/api/groups",model:a});return{Group:a,Groups:b}}); +//# sourceMappingURL=../../../maps/mvc/groups/group-model.js.map \ No newline at end of file diff --git a/static/scripts/mvc/groups/group.model.js b/static/scripts/mvc/groups/group.model.js new file mode 100644 index 00000000000..b050bdad096 --- /dev/null +++ b/static/scripts/mvc/groups/group.model.js @@ -0,0 +1,2 @@ +define([],function(){var a=Backbone.Model.extend({urlRoot:"/api/groups/"}),b=Backbone.Collection.extend({url:"/api/groups",model:a});return{Group:a,Groups:b}}); +//# sourceMappingURL=../../../maps/mvc/groups/group.model.js.map \ No newline at end of file diff --git a/static/scripts/toolshed.groups.js b/static/scripts/toolshed.groups.js new file mode 100644 index 00000000000..4c0663b9afb --- /dev/null +++ b/static/scripts/toolshed.groups.js @@ -0,0 +1,2 @@ +define(["mvc/groups/group-list-view","mvc/groups/group-detail-view","mvc/groups/group-model"],function(a,b){var c=Backbone.Router.extend({routes:{"":"groups",":group_id":"group_page"}}),d=Backbone.View.extend({groupListView:null,groupDetailView:null,collection:null,initialize:function(){window.globalTS.groups=this,this.ts_router=new c,this.ts_router.on("route:groups",function(){window.globalTS.groups.groupListView=new a.GroupListView}),this.ts_router.on("route:group_page",function(a){window.globalTS.groups.groupDetailView=new b.GroupDetailView({group_id:a})}),Backbone.history.start({pushState:!1})}});return{ToolshedGroups:d}}); +//# sourceMappingURL=../maps/toolshed.groups.js.map \ No newline at end of file diff --git a/templates/grid_base.mako b/templates/grid_base.mako index 02cac200788..80a92c91a12 100644 --- a/templates/grid_base.mako +++ b/templates/grid_base.mako @@ -6,7 +6,6 @@ if kwargs.get( 'embedded', False ): # No inheritance - using only embeddable content (self.body) return None - if context.get('use_panels'): if context.get('webapp'): app_name = context.get('webapp') diff --git a/templates/webapps/tool_shed/base_panels.mako b/templates/webapps/tool_shed/base_panels.mako index 97b980d7090..48149204112 100644 --- a/templates/webapps/tool_shed/base_panels.mako +++ b/templates/webapps/tool_shed/base_panels.mako @@ -80,6 +80,9 @@ ## Repositories tab. ${tab( "repositories", "Repositories", h.url_for( controller='/repository', action='index' ) )} + ## Groups tab. + ${tab( "groups", "Groups", h.url_for( controller='/groups', action='index' ) )} + ## Admin tab. ${tab( "admin", "Admin", h.url_for( controller='/admin', action='index' ), extra_class="admin-only", visible=( trans.user and app.config.is_admin_user( trans.user ) ) )} diff --git a/templates/webapps/tool_shed/group/index.mako b/templates/webapps/tool_shed/group/index.mako new file mode 100644 index 00000000000..e070faec5ee --- /dev/null +++ b/templates/webapps/tool_shed/group/index.mako @@ -0,0 +1,50 @@ +<%inherit file="/webapps/tool_shed/base_panels.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="stylesheets()"> + ## Include "base.css" for styling tool menu and forms (details) + ${h.css( "base", "autocomplete_tagging", "tool_menu" )} + + ## But make sure styles for the layout take precedence + ${parent.stylesheets()} + + + + + +<%def name="javascripts()"> + ${parent.javascripts()} + + +<%def name="init()"> + %if trans.app.config.require_login and not trans.user: + + %endif + + +<%def name="center_panel()"> + +
+