diff --git a/kallithea/config/routing.py b/kallithea/config/routing.py --- a/kallithea/config/routing.py +++ b/kallithea/config/routing.py @@ -99,6 +99,8 @@ def make_map(config): rmap.connect('about', '/about', controller='home', action='about') rmap.connect('repo_switcher_data', '/_repos', controller='home', action='repo_switcher_data') + rmap.connect('users_and_groups_data', '/_users_and_groups', controller='home', + action='users_and_groups_data') rmap.connect('rst_help', "http://docutils.sourceforge.net/docs/user/rst/quickref.html", diff --git a/kallithea/controllers/home.py b/kallithea/controllers/home.py --- a/kallithea/controllers/home.py +++ b/kallithea/controllers/home.py @@ -32,13 +32,15 @@ from tg import tmpl_context as c, reques from tg.i18n import ugettext as _ from webob.exc import HTTPBadRequest from sqlalchemy.sql.expression import func +from sqlalchemy import or_, and_ from kallithea.lib.utils import conditional_cache from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator from kallithea.lib.base import BaseController, render, jsonify -from kallithea.model.db import Repository, RepoGroup +from kallithea.lib import helpers as h +from kallithea.model.db import Repository, RepoGroup, User, UserGroup from kallithea.model.repo import RepoModel - +from kallithea.model.scm import UserGroupList log = logging.getLogger(__name__) @@ -142,3 +144,69 @@ class HomeController(BaseController): 'results': res } return data + + @LoginRequired() + @jsonify + def users_and_groups_data(self): + """ + Returns 'results' with a list of users and user groups. + + You can either use the 'key' GET parameter to get a user by providing + the exact user key or you can use the 'query' parameter to + search for users by user key, first name and last name. + 'types' defaults to just 'users' but can be set to 'users,groups' to + get both users and groups. + No more than 500 results (of each kind) will be returned. + """ + types = request.GET.get('types', 'users').split(',') + key = request.GET.get('key', '') + query = request.GET.get('query', '') + results = [] + if 'users' in types: + user_list = [] + if key: + u = User.get_by_username(key) + if u: + user_list = [u] + elif query: + user_list = User.query() \ + .filter(User.is_default_user == False) \ + .filter(User.active == True) \ + .filter(or_( + User.username.like("%%"+query+"%%"), + User.name.like("%%"+query+"%%"), + User.lastname.like("%%"+query+"%%"), + )) \ + .order_by(User.username) \ + .limit(500) \ + .all() + for u in user_list: + results.append({ + 'type': 'user', + 'id': u.user_id, + 'nname': u.username, + 'fname': u.name, + 'lname': u.lastname, + 'gravatar_lnk': h.gravatar_url(u.email, size=28, default='default'), + 'gravatar_size': 14, + }) + if 'groups' in types: + grp_list = [] + if key: + grp = UserGroup.get_by_group_name(key) + if grp: + grp_list = [grp] + elif query: + grp_list = UserGroup.query() \ + .filter(UserGroup.users_group_name.like("%%"+query+"%%")) \ + .filter(UserGroup.users_group_active == True) \ + .order_by(UserGroup.users_group_name) \ + .limit(500) \ + .all() + for g in UserGroupList(grp_list, perm_level='read'): + results.append({ + 'type': 'group', + 'id': g.users_group_id, + 'grname': g.users_group_name, + }) + return dict(results=results) diff --git a/kallithea/public/js/base.js b/kallithea/public/js/base.js --- a/kallithea/public/js/base.js +++ b/kallithea/public/js/base.js @@ -1094,7 +1094,7 @@ var autocompleteFormatter = function (oR query = sResultMatch.term.toLowerCase(); // group - if (oResultData.grname) { + if (oResultData.type == "group") { return autocompleteGravatar( "{0}: {1}".format( _TM['Group'], @@ -1118,20 +1118,34 @@ var autocompleteFormatter = function (oR return ''; }; -var SimpleUserAutoComplete = function ($inputElement, users_list) { - $inputElement.select2( - { +var SimpleUserAutoComplete = function ($inputElement) { + $inputElement.select2({ formatInputTooShort: $inputElement.attr('placeholder'), initSelection : function (element, callback) { - var val = $inputElement.val(); - $.each(users_list, function(i, user) { - if (user.nname == val) - callback(user); + $.ajax({ + url: pyroutes.url('users_and_groups_data'), + dataType: 'json', + data: { + key: element.val() + }, + success: function(data){ + callback(data.results[0]); + } }); }, minimumInputLength: 1, - query: function (query) { - query.callback({results: autocompleteMatchUsers(query.term, users_list)}); + ajax: { + url: pyroutes.url('users_and_groups_data'), + dataType: 'json', + data: function(term, page){ + return { + query: term + }; + }, + results: function (data, page){ + return data; + }, + cache: true }, formatSelection: autocompleteFormatter, formatResult: autocompleteFormatter, @@ -1140,31 +1154,32 @@ var SimpleUserAutoComplete = function ($ }); } -var MembersAutoComplete = function ($inputElement, $typeElement, users_list, groups_list) { +var MembersAutoComplete = function ($inputElement, $typeElement) { - var matchAll = function (sQuery) { - var u = autocompleteMatchUsers(sQuery, users_list); - var g = autocompleteMatchGroups(sQuery, groups_list); - return u.concat(g); - }; - - $inputElement.select2( - { + $inputElement.select2({ placeholder: $inputElement.attr('placeholder'), minimumInputLength: 1, - query: function (query) { - query.callback({results: matchAll(query.term)}); + ajax: { + url: pyroutes.url('users_and_groups_data'), + dataType: 'json', + data: function(term, page){ + return { + query: term, + types: 'users,groups' + }; + }, + results: function (data, page){ + return data; + }, + cache: true }, formatSelection: autocompleteFormatter, formatResult: autocompleteFormatter, escapeMarkup: function(m) { return m; }, + id: function(item) { return item.type == 'user' ? item.nname : item.grname }, }).on("select2-selecting", function(e) { // e.choice.id is automatically used as selection value - just set the type of the selection - if (e.choice.nname != undefined) { - $typeElement.val('user'); - } else { - $typeElement.val('users_group'); - } + $typeElement.val(e.choice.type); }); } @@ -1292,13 +1307,23 @@ var removeReviewMember = function(review } /* activate auto completion of users as PR reviewers */ -var PullRequestAutoComplete = function ($inputElement, users_list) { +var PullRequestAutoComplete = function ($inputElement) { $inputElement.select2( { placeholder: $inputElement.attr('placeholder'), minimumInputLength: 1, - query: function (query) { - query.callback({results: autocompleteMatchUsers(query.term, users_list)}); + ajax: { + url: pyroutes.url('users_and_groups_data'), + dataType: 'json', + data: function(term, page){ + return { + query: term + }; + }, + results: function (data, page){ + return data; + }, + cache: true }, formatSelection: autocompleteFormatter, formatResult: autocompleteFormatter, @@ -1312,7 +1337,7 @@ var PullRequestAutoComplete = function ( } -function addPermAction(perm_type, users_list, groups_list) { +function addPermAction(perm_type) { var template = '