##// END OF EJS Templates
repo-switcher: new unified search box for filtering/accessing users, repos and repo groups....
marcink -
r2774:a8ecef4e default
parent child Browse files
Show More
@@ -19,22 +19,44 b''
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 def assert_and_get_content(result):
22 def assert_and_get_main_filter_content(result):
23 23 repos = []
24 24 groups = []
25 25 commits = []
26 users = []
27 for data_item in result:
28 assert data_item['id']
29 assert data_item['value']
30 assert data_item['value_display']
31 assert data_item['url']
32
33 if data_item['type'] == 'search':
34 assert data_item['value_display'].startswith('Full text search for:')
35 elif data_item['type'] == 'repo':
36 repos.append(data_item)
37 elif data_item['type'] == 'repo_group':
38 groups.append(data_item)
39 elif data_item['type'] == 'user':
40 users.append(data_item)
41 elif data_item['type'] == 'commit':
42 commits.append(data_item)
43 else:
44 raise Exception('invalid type `%s`' % data_item['type'])
45
46 return repos, groups, users, commits
47
48
49 def assert_and_get_repo_list_content(result):
50 repos = []
26 51 for data in result:
27 52 for data_item in data['children']:
28 53 assert data_item['id']
29 54 assert data_item['text']
30 55 assert data_item['url']
56
31 57 if data_item['type'] == 'repo':
32 58 repos.append(data_item)
33 elif data_item['type'] == 'group':
34 groups.append(data_item)
35 elif data_item['type'] == 'commit':
36 commits.append(data_item)
37 59 else:
38 60 raise Exception('invalid type %s' % data_item['type'])
39 61
40 return repos, groups, commits No newline at end of file
62 return repos
@@ -22,7 +22,7 b' import json'
22 22
23 23 import pytest
24 24
25 from . import assert_and_get_content
25 from . import assert_and_get_main_filter_content
26 26 from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN
27 27 from rhodecode.tests.fixture import Fixture
28 28
@@ -103,19 +103,15 b' class TestGotoSwitcherData(TestControlle'
103 103 RepoGroupModel().delete(el, force_delete=True)
104 104 Session().commit()
105 105
106 def test_returns_list_of_repos_and_groups(self, xhr_header):
106 def test_empty_query(self, xhr_header):
107 107 self.log_user()
108 108
109 109 response = self.app.get(
110 110 route_path('goto_switcher_data'),
111 111 extra_environ=xhr_header, status=200)
112 result = json.loads(response.body)['results']
113
114 repos, groups, commits = assert_and_get_content(result)
112 result = json.loads(response.body)['suggestions']
115 113
116 assert len(repos) == len(Repository.get_all())
117 assert len(groups) == len(RepoGroup.get_all())
118 assert len(commits) == 0
114 assert result == []
119 115
120 116 def test_returns_list_of_repos_and_groups_filtered(self, xhr_header):
121 117 self.log_user()
@@ -124,14 +120,47 b' class TestGotoSwitcherData(TestControlle'
124 120 route_path('goto_switcher_data'),
125 121 params={'query': 'abc'},
126 122 extra_environ=xhr_header, status=200)
127 result = json.loads(response.body)['results']
123 result = json.loads(response.body)['suggestions']
128 124
129 repos, groups, commits = assert_and_get_content(result)
125 repos, groups, users, commits = assert_and_get_main_filter_content(result)
130 126
131 127 assert len(repos) == 13
132 128 assert len(groups) == 5
129 assert len(users) == 0
133 130 assert len(commits) == 0
134 131
132 def test_returns_list_of_users_filtered(self, xhr_header):
133 self.log_user()
134
135 response = self.app.get(
136 route_path('goto_switcher_data'),
137 params={'query': 'user:admin'},
138 extra_environ=xhr_header, status=200)
139 result = json.loads(response.body)['suggestions']
140
141 repos, groups, users, commits = assert_and_get_main_filter_content(result)
142
143 assert len(repos) == 0
144 assert len(groups) == 0
145 assert len(users) == 1
146 assert len(commits) == 0
147
148 def test_returns_list_of_commits_filtered(self, xhr_header):
149 self.log_user()
150
151 response = self.app.get(
152 route_path('goto_switcher_data'),
153 params={'query': 'commit:e8'},
154 extra_environ=xhr_header, status=200)
155 result = json.loads(response.body)['suggestions']
156
157 repos, groups, users, commits = assert_and_get_main_filter_content(result)
158
159 assert len(repos) == 0
160 assert len(groups) == 0
161 assert len(users) == 0
162 assert len(commits) == 5
163
135 164 def test_returns_list_of_properly_sorted_and_filtered(self, xhr_header):
136 165 self.log_user()
137 166
@@ -139,13 +168,13 b' class TestGotoSwitcherData(TestControlle'
139 168 route_path('goto_switcher_data'),
140 169 params={'query': 'abc'},
141 170 extra_environ=xhr_header, status=200)
142 result = json.loads(response.body)['results']
171 result = json.loads(response.body)['suggestions']
143 172
144 repos, groups, commits = assert_and_get_content(result)
173 repos, groups, users, commits = assert_and_get_main_filter_content(result)
145 174
146 test_repos = [x['text'] for x in repos[:4]]
175 test_repos = [x['value_display'] for x in repos[:4]]
147 176 assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
148 177
149 test_groups = [x['text'] for x in groups[:4]]
178 test_groups = [x['value_display'] for x in groups[:4]]
150 179 assert ['abc_repos', 'repos_abc',
151 180 'forked-abc', 'forked-abc/a'] == test_groups
@@ -20,7 +20,7 b''
20 20
21 21 import json
22 22
23 from . import assert_and_get_content
23 from . import assert_and_get_repo_list_content
24 24 from rhodecode.tests import TestController
25 25 from rhodecode.tests.fixture import Fixture
26 26 from rhodecode.model.db import Repository
@@ -50,11 +50,9 b' class TestRepoListData(TestController):'
50 50 extra_environ=xhr_header, status=200)
51 51 result = json.loads(response.body)['results']
52 52
53 repos, groups, commits = assert_and_get_content(result)
53 repos = assert_and_get_repo_list_content(result)
54 54
55 55 assert len(repos) == len(Repository.get_all())
56 assert len(groups) == 0
57 assert len(commits) == 0
58 56
59 57 def test_returns_list_of_repos_and_groups_filtered(self, xhr_header):
60 58 self.log_user()
@@ -65,12 +63,10 b' class TestRepoListData(TestController):'
65 63 extra_environ=xhr_header, status=200)
66 64 result = json.loads(response.body)['results']
67 65
68 repos, groups, commits = assert_and_get_content(result)
66 repos = assert_and_get_repo_list_content(result)
69 67
70 68 assert len(repos) == len(Repository.query().filter(
71 69 Repository.repo_name.ilike('%vcs_test_git%')).all())
72 assert len(groups) == 0
73 assert len(commits) == 0
74 70
75 71 def test_returns_list_of_repos_and_groups_filtered_with_type(self, xhr_header):
76 72 self.log_user()
@@ -81,12 +77,10 b' class TestRepoListData(TestController):'
81 77 extra_environ=xhr_header, status=200)
82 78 result = json.loads(response.body)['results']
83 79
84 repos, groups, commits = assert_and_get_content(result)
80 repos = assert_and_get_repo_list_content(result)
85 81
86 82 assert len(repos) == len(Repository.query().filter(
87 83 Repository.repo_name.ilike('%vcs_test_git%')).all())
88 assert len(groups) == 0
89 assert len(commits) == 0
90 84
91 85 def test_returns_list_of_repos_non_ascii_query(self, xhr_header):
92 86 self.log_user()
@@ -96,8 +90,6 b' class TestRepoListData(TestController):'
96 90 extra_environ=xhr_header, status=200)
97 91 result = json.loads(response.body)['results']
98 92
99 repos, groups, commits = assert_and_get_content(result)
93 repos = assert_and_get_repo_list_content(result)
100 94
101 95 assert len(repos) == 0
102 assert len(groups) == 0
103 assert len(commits) == 0
@@ -20,6 +20,7 b''
20 20
21 21 import re
22 22 import logging
23 import collections
23 24
24 25 from pyramid.view import view_config
25 26
@@ -31,7 +32,7 b' from rhodecode.lib.index import searcher'
31 32 from rhodecode.lib.utils2 import safe_unicode, str2bool
32 33 from rhodecode.lib.ext_json import json
33 34 from rhodecode.model.db import (
34 func, or_, in_filter_generator, Repository, RepoGroup)
35 func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
35 36 from rhodecode.model.repo import RepoModel
36 37 from rhodecode.model.repo_group import RepoGroupModel
37 38 from rhodecode.model.scm import RepoGroupList, RepoList
@@ -104,6 +105,7 b' class HomeView(BaseAppView):'
104 105 return {'suggestions': _user_groups}
105 106
106 107 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
108 org_query = name_contains
107 109 allowed_ids = self._rhodecode_user.repo_acl_ids(
108 110 ['repository.read', 'repository.write', 'repository.admin'],
109 111 cache=False, name_filter=name_contains) or [-1]
@@ -125,20 +127,24 b' class HomeView(BaseAppView):'
125 127 Repository.repo_name.ilike(ilike_expression))
126 128 query = query.limit(limit)
127 129
128 acl_repo_iter = query
130 acl_iter = query
129 131
130 132 return [
131 133 {
132 134 'id': obj.repo_name,
135 'value': org_query,
136 'value_display': obj.repo_name,
133 137 'text': obj.repo_name,
134 138 'type': 'repo',
135 'obj': {'repo_type': obj.repo_type, 'private': obj.private,
136 'repo_id': obj.repo_id},
139 'repo_id': obj.repo_id,
140 'repo_type': obj.repo_type,
141 'private': obj.private,
137 142 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
138 143 }
139 for obj in acl_repo_iter]
144 for obj in acl_iter]
140 145
141 146 def _get_repo_group_list(self, name_contains=None, limit=20):
147 org_query = name_contains
142 148 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
143 149 ['group.read', 'group.write', 'group.admin'],
144 150 cache=False, name_filter=name_contains) or [-1]
@@ -157,20 +163,56 b' class HomeView(BaseAppView):'
157 163 RepoGroup.group_name.ilike(ilike_expression))
158 164 query = query.limit(limit)
159 165
160 acl_repo_iter = query
166 acl_iter = query
161 167
162 168 return [
163 169 {
164 170 'id': obj.group_name,
165 'text': obj.group_name,
166 'type': 'group',
167 'obj': {},
171 'value': org_query,
172 'value_display': obj.group_name,
173 'type': 'repo_group',
168 174 'url': h.route_path(
169 175 'repo_group_home', repo_group_name=obj.group_name)
170 176 }
171 for obj in acl_repo_iter]
177 for obj in acl_iter]
178
179 def _get_user_list(self, name_contains=None, limit=20):
180 org_query = name_contains
181 if not name_contains:
182 return []
183
184 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
185 if len(name_contains) != 1:
186 return []
187 name_contains = name_contains[0]
188
189 query = User.query()\
190 .order_by(func.length(User.username))\
191 .order_by(User.username) \
192 .filter(User.username != User.DEFAULT_USER)
172 193
173 def _get_hash_commit_list(self, auth_user, query=None):
194 if name_contains:
195 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
196 query = query.filter(
197 User.username.ilike(ilike_expression))
198 query = query.limit(limit)
199
200 acl_iter = query
201
202 return [
203 {
204 'id': obj.user_id,
205 'value': org_query,
206 'value_display': obj.username,
207 'type': 'user',
208 'icon_link': h.gravatar_url(obj.email, 30),
209 'url': h.route_path(
210 'user_profile', username=obj.username)
211 }
212 for obj in acl_iter]
213
214 def _get_hash_commit_list(self, auth_user, query):
215 org_query = query
174 216 if not query or len(query) < 3:
175 217 return []
176 218
@@ -178,20 +220,21 b' class HomeView(BaseAppView):'
178 220
179 221 if len(commit_hashes) != 1:
180 222 return []
181
182 commit_hash_prefix = commit_hashes[0]
223 commit_hash = commit_hashes[0]
183 224
184 225 searcher = searcher_from_config(self.request.registry.settings)
185 226 result = searcher.search(
186 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
227 'commit_id:%s*' % commit_hash, 'commit', auth_user,
187 228 raise_on_exc=False)
188 229
189 230 return [
190 231 {
191 232 'id': entry['commit_id'],
192 'text': entry['commit_id'],
233 'value': org_query,
234 'value_display': 'repo `{}` commit: {}'.format(
235 entry['repository'], entry['commit_id']),
193 236 'type': 'commit',
194 'obj': {'repo': entry['repository']},
237 'repo': entry['repository'],
195 238 'url': h.route_path(
196 239 'repo_commit',
197 240 repo_name=entry['repository'], commit_id=entry['commit_id'])
@@ -235,41 +278,47 b' class HomeView(BaseAppView):'
235 278 _ = self.request.translate
236 279
237 280 query = self.request.GET.get('query')
238 log.debug('generating goto switcher list, query %s', query)
281 log.debug('generating main filter data, query %s', query)
282
283 default_search_val = 'Full text search for: `{}`'.format(query)
284 res = []
285 if not query:
286 return {'suggestions': res}
239 287
240 res = []
288 res.append({
289 'id': -1,
290 'value': query,
291 'value_display': default_search_val,
292 'type': 'search',
293 'url': h.route_path(
294 'search', _query={'q': query})
295 })
296
241 297 repo_groups = self._get_repo_group_list(query)
242 if repo_groups:
243 res.append({
244 'text': _('Groups'),
245 'children': repo_groups
246 })
298 for serialized_repo_group in repo_groups:
299 res.append(serialized_repo_group)
247 300
248 301 repos = self._get_repo_list(query)
249 if repos:
250 res.append({
251 'text': _('Repositories'),
252 'children': repos
253 })
302 for serialized_repo in repos:
303 res.append(serialized_repo)
304
305 # TODO(marcink): permissions for that ?
306 users = self._get_user_list(query)
307 for serialized_user in users:
308 res.append(serialized_user)
254 309
255 310 commits = self._get_hash_commit_list(c.auth_user, query)
256 311 if commits:
257 unique_repos = {}
312 unique_repos = collections.OrderedDict()
258 313 for commit in commits:
259 unique_repos.setdefault(commit['obj']['repo'], []
260 ).append(commit)
314 repo_name = commit['repo']
315 unique_repos.setdefault(repo_name, []).append(commit)
261 316
262 for repo in unique_repos:
263 res.append({
264 'text': _('Commits in %(repo)s') % {'repo': repo},
265 'children': unique_repos[repo]
266 })
317 for repo, commits in unique_repos.items():
318 for commit in commits:
319 res.append(commit)
267 320
268 data = {
269 'more': False,
270 'results': res
271 }
272 return data
321 return {'suggestions': res}
273 322
274 323 def _get_groups_and_repos(self, repo_group_id=None):
275 324 # repo groups groups
@@ -766,7 +766,10 b' class RepoPullRequestsView(RepoAppView, '
766 766 'id': obj['name'],
767 767 'text': obj['name'],
768 768 'type': 'repo',
769 'obj': obj['dbrepo']
769 'repo_id': obj['dbrepo']['repo_id'],
770 'repo_type': obj['dbrepo']['repo_type'],
771 'private': obj['dbrepo']['private'],
772
770 773 })
771 774
772 775 data = {
@@ -281,11 +281,9 b''
281 281 }
282 282
283 283 .navigation li.open {
284
285 .submenu,
286 .repo_switcher {
287 display: block;
288 }
284 .submenu {
285 display: block;
286 }
289 287 }
290 288
291 289 .navigation li:last-child .submenu {
@@ -642,3 +640,45 b' ul#context-pages {'
642 640
643 641 }
644 642 }
643
644 .main_filter_help_box {
645 padding: 7px 7px;
646 border-top: 1px solid @grey4;
647 border-right: 1px solid @grey4;
648 border-bottom: 1px solid @grey4;
649 display: inline-block;
650 vertical-align: top;
651 margin-left: -5px;
652 background: @grey3;
653 }
654
655 .main_filter_input_box {
656 display: inline-block;
657 }
658
659 .main_filter_box {
660 margin: 9px 0 0 0;
661 }
662
663 #main_filter_help {
664 background: @grey3;
665 border: 1px solid black;
666 position: absolute;
667 white-space: pre-wrap;
668 z-index: 9999;
669 color: @nav-grey;
670 margin: 1px 7px;
671 padding: 0 2px;
672 }
673
674 .main_filter_input {
675 padding: 6px;
676 min-width: 220px;
677 color: @nav-grey;
678 background: @grey3;
679 }
680
681 .main_filter_input::placeholder {
682 color: @nav-grey;
683 opacity: 1;
684 }
@@ -228,7 +228,7 b' mark,'
228 228 clear: both;
229 229 float: left;
230 230 width: 100%;
231 margin: @pagepadding 0 @pagepadding;
231 margin: @pagepadding/2 0 @pagepadding;
232 232
233 233 .breadcrumbs{
234 234 float: left;
@@ -20,7 +20,7 b' function setRCMouseBindings(repoName, re'
20 20
21 21 // / open the quick filter
22 22 Mousetrap.bind(['/'], function(e) {
23 $('#repo_switcher').select2('open');
23 $('#main_filter').get(0).focus();
24 24
25 25 // return false to prevent default browser behavior
26 26 // and stop event from bubbling
@@ -131,7 +131,7 b' var repoFilter = function(data) {'
131 131
132 132 $.each(data.results[0].children, function() {
133 133 // replace name to ID for submision
134 this.id = this.obj.repo_id;
134 this.id = this.repo_id;
135 135 results.push(this);
136 136 });
137 137
@@ -154,7 +154,7 b' var selectVcsScope = function() {'
154 154 dropdownAutoWidth: true,
155 155 containerCssClass: "drop-menu",
156 156 dropdownCssClass: "drop-menu-dropdown",
157 formatResult: formatResult,
157 formatResult: formatRepoResult,
158 158 query: $.debounce(250, function(query){
159 159 self = this;
160 160 var cacheKey = query.term;
@@ -165,8 +165,8 b' var repoTypeFilter = function(data) {'
165 165
166 166 $.each(data.results[0].children, function() {
167 167 // filter out the SAME repo, it cannot be used as fork of itself
168 if (this.obj.repo_id != currentRepoId) {
169 this.id = this.obj.repo_id;
168 if (this.repo_id != currentRepoId) {
169 this.id = this.repo_id;
170 170 results.push(this)
171 171 }
172 172 });
@@ -181,7 +181,7 b' var repoTypeFilter = function(data) {'
181 181 dropdownAutoWidth: true,
182 182 containerCssClass: "drop-menu",
183 183 dropdownCssClass: "drop-menu-dropdown",
184 formatResult: formatResult,
184 formatResult: formatRepoResult,
185 185 query: $.debounce(250, function(query){
186 186 self = this;
187 187 var cacheKey = query.term;
@@ -129,7 +129,7 b' var repoFilter = function(data) {'
129 129
130 130 $.each(data.results[0].children, function() {
131 131 // replace name to ID for submision
132 this.id = this.obj.repo_id;
132 this.id = this.repo_id;
133 133 results.push(this);
134 134 });
135 135
@@ -152,7 +152,7 b' var selectVcsScope = function() {'
152 152 dropdownAutoWidth: true,
153 153 containerCssClass: "drop-menu",
154 154 dropdownCssClass: "drop-menu-dropdown",
155 formatResult: formatResult,
155 formatResult: formatRepoResult,
156 156 query: $.debounce(250, function(query){
157 157 self = this;
158 158 var cacheKey = query.term;
@@ -226,7 +226,7 b''
226 226 <!--- CONTEXT BAR -->
227 227 <div id="context-bar">
228 228 <div class="wrapper">
229 <ul id="context-pages" class="horizontal-list navigation">
229 <ul id="context-pages" class="navigation horizontal-list">
230 230 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
231 231 <li class="${is_active('changelog')}"><a class="menulink" href="${h.route_path('repo_changelog', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
232 232 <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li>
@@ -386,10 +386,30 b''
386 386 return ""
387 387 %>
388 388 <ul id="quick" class="main_nav navigation horizontal-list">
389 <!-- repo switcher -->
390 <li class="${is_active('repositories')} repo_switcher_li has_select2">
391 <input id="repo_switcher" name="repo_switcher" type="hidden">
392 </li>
389
390 ## Main filter
391 <li>
392 <div class="menulabel main_filter_box">
393 <div class="main_filter_input_box">
394 <input class="main_filter_input" id="main_filter" size="15" type="text" name="main_filter" placeholder="${_('search / go to...')}" value=""/>
395 </div>
396 <div class="main_filter_help_box">
397 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
398 </div>
399 </div>
400
401 <div id="main_filter_help" style="display: none">
402 Use '/' key to quickly access this field.
403 Enter name of repository, or repository group for quick search.
404
405 Prefix query to allow special search:
406
407 For usernames, e.g user:admin
408
409 For commit hash/id, e.g commit:efced4
410
411 </div>
412 </li>
393 413
394 414 ## ROOT MENU
395 415 %if c.rhodecode_user.username != h.DEFAULT_USER:
@@ -432,6 +452,9 b''
432 452 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
433 453 </li>
434 454 % endif
455 ## render extra user menu
456 ${usermenu(active=(active=='my_account'))}
457
435 458 % if c.debug_style:
436 459 <li class="${is_active('debug_style')}">
437 460 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
@@ -439,102 +462,127 b''
439 462 </a>
440 463 </li>
441 464 % endif
442 ## render extra user menu
443 ${usermenu(active=(active=='my_account'))}
444 465 </ul>
445 466
446 467 <script type="text/javascript">
447 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
448
449 /*format the look of items in the list*/
450 var format = function(state, escapeMarkup){
451 if (!state.id){
452 return state.text; // optgroup
453 }
454 var obj_dict = state.obj;
455 var tmpl = '';
468 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
456 469
457 if(obj_dict && state.type == 'repo'){
458 if(obj_dict['repo_type'] === 'hg'){
459 tmpl += '<i class="icon-hg"></i> ';
460 }
461 else if(obj_dict['repo_type'] === 'git'){
462 tmpl += '<i class="icon-git"></i> ';
463 }
464 else if(obj_dict['repo_type'] === 'svn'){
465 tmpl += '<i class="icon-svn"></i> ';
470 var formatRepoResult = function(result, container, query, escapeMarkup) {
471 return function(data, escapeMarkup) {
472 if (!data.repo_id){
473 return data.text; // optgroup text Repositories
466 474 }
467 if(obj_dict['private']){
468 tmpl += '<i class="icon-lock" ></i> ';
469 }
470 else if(visual_show_public_icon){
471 tmpl += '<i class="icon-unlock-alt"></i> ';
475
476 var tmpl = '';
477 var repoType = data['repo_type'];
478 var repoName = data['text'];
479
480 if(data && data.type == 'repo'){
481 if(repoType === 'hg'){
482 tmpl += '<i class="icon-hg"></i> ';
483 }
484 else if(repoType === 'git'){
485 tmpl += '<i class="icon-git"></i> ';
486 }
487 else if(repoType === 'svn'){
488 tmpl += '<i class="icon-svn"></i> ';
489 }
490 if(data['private']){
491 tmpl += '<i class="icon-lock" ></i> ';
492 }
493 else if(visualShowPublicIcon){
494 tmpl += '<i class="icon-unlock-alt"></i> ';
495 }
472 496 }
473 }
474 if(obj_dict && state.type == 'commit') {
475 tmpl += '<i class="icon-tag"></i>';
476 }
477 if(obj_dict && state.type == 'group'){
478 tmpl += '<i class="icon-folder-close"></i> ';
479 }
480 tmpl += escapeMarkup(state.text);
481 return tmpl;
482 };
497 tmpl += escapeMarkup(repoName);
498 return tmpl;
483 499
484 var formatResult = function(result, container, query, escapeMarkup) {
485 return format(result, escapeMarkup);
486 };
487
488 var formatSelection = function(data, container, escapeMarkup) {
489 return format(data, escapeMarkup);
500 }(result, escapeMarkup);
490 501 };
491 502
492 $("#repo_switcher").select2({
493 cachedDataSource: {},
494 minimumInputLength: 2,
495 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
496 dropdownAutoWidth: true,
497 formatResult: formatResult,
498 formatSelection: formatSelection,
499 containerCssClass: "repo-switcher",
500 dropdownCssClass: "repo-switcher-dropdown",
501 escapeMarkup: function(m){
502 // don't escape our custom placeholder
503 if(m.substr(0,23) == '<div class="menulabel">'){
504 return m;
505 }
503
504 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
505
506 if (value.split(':').length === 2) {
507 value = value.split(':')[1]
508 }
509
510 var searchType = data['type'];
511 var valueDisplay = data['value_display'];
512
513 var escapeRegExChars = function (value) {
514 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
515 };
516 var pattern = '(' + escapeRegExChars(value) + ')';
517
518 // highlight match
519 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
520 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
521
522 var icon = '';
506 523
507 return Select2.util.escapeMarkup(m);
508 },
509 query: $.debounce(250, function(query){
510 self = this;
511 var cacheKey = query.term;
512 var cachedData = self.cachedDataSource[cacheKey];
524 if (searchType === 'search') {
525 icon += '<i class="icon-more"></i> ';
526 }
527 else if (searchType === 'repo') {
528 if (data['repo_type'] === 'hg') {
529 icon += '<i class="icon-hg"></i> ';
530 }
531 else if (data['repo_type'] === 'git') {
532 icon += '<i class="icon-git"></i> ';
533 }
534 else if (data['repo_type'] === 'svn') {
535 icon += '<i class="icon-svn"></i> ';
536 }
537 if (data['private']) {
538 icon += '<i class="icon-lock" ></i> ';
539 }
540 else if (visualShowPublicIcon) {
541 icon += '<i class="icon-unlock-alt"></i> ';
542 }
543 }
544 else if (searchType === 'repo_group') {
545 icon += '<i class="icon-folder-close"></i> ';
546 }
547 else if (searchType === 'user') {
548 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
549 }
550 else if (searchType === 'commit') {
551 icon += '<i class="icon-tag"></i>';
552 }
513 553
514 if (cachedData) {
515 query.callback({results: cachedData.results});
516 } else {
517 $.ajax({
518 url: pyroutes.url('goto_switcher_data'),
519 data: {'query': query.term},
520 dataType: 'json',
521 type: 'GET',
522 success: function(data) {
523 self.cachedDataSource[cacheKey] = data;
524 query.callback({results: data.results});
525 },
526 error: function(data, textStatus, errorThrown) {
527 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
528 }
529 })
530 }
531 })
554 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
555 return tmpl.format(icon, valueDisplay);
556 };
557
558 var handleSelect = function(element, suggestion) {
559 window.location = suggestion['url'];
560 };
561 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
562 if (queryLowerCase.split(':').length === 2) {
563 queryLowerCase = queryLowerCase.split(':')[1]
564 }
565 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
566 };
567
568 $('#main_filter').autocomplete({
569 serviceUrl: pyroutes.url('goto_switcher_data'),
570 minChars:2,
571 maxHeight:400,
572 deferRequestBy: 300, //miliseconds
573 tabDisabled: true,
574 autoSelectFirst: true,
575 formatResult: autocompleteMainFilterFormatResult,
576 lookupFilter: autocompleteMainFilterResult,
577 onSelect: function(element, suggestion){
578 handleSelect(element, suggestion);
579 return false;
580 }
532 581 });
533 582
534 $("#repo_switcher").on('select2-selecting', function(e){
535 e.preventDefault();
536 window.location = e.choice.url;
537 });
583 showMainFilterBox = function () {
584 $('#main_filter_help').toggle();
585 }
538 586
539 587 </script>
540 588 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
@@ -557,7 +605,7 b''
557 605 </tr>
558 606 <%
559 607 elems = [
560 ('/', 'Open quick search box'),
608 ('/', 'Use quick search box'),
561 609 ('g h', 'Goto home page'),
562 610 ('g g', 'Goto my private gists page'),
563 611 ('g G', 'Goto my public gists page'),
@@ -611,3 +659,4 b''
611 659 </div><!-- /.modal-content -->
612 660 </div><!-- /.modal-dialog -->
613 661 </div><!-- /.modal -->
662
@@ -5,9 +5,8 b''
5 5 <!-- box / title -->
6 6 <div class="title">
7 7 <div class="block-left breadcrumbs">
8 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
9 8 ${self.breadcrumbs()}
10 <span id="match_container" style="display:none">&raquo; <span id="match_count">0</span> ${_('matches')}</span>
9 <span id="match_container" style="display:none"><span id="match_count">0</span> ${_('matches')}</span>
11 10 </div>
12 11 %if c.rhodecode_user.username != h.DEFAULT_USER:
13 12 <div class="block-right">
@@ -137,65 +136,6 b''
137 136 });
138 137 % endif
139 138
140 var getDatatableCount = function() {
141 var reposCount = 0;
142 var reposCountTotal = 0;
143
144 % if c.repos_data != '[]':
145 var pageInfo = $('#repo_list_table').dataTable().api().page.info();
146 var reposCount = pageInfo.recordsDisplay;
147 var reposCountTotal = pageInfo.recordsTotal;
148 % endif
149
150 var repoGroupsCount = 0;
151 var repoGroupsCountTotal = 0;
152
153 % if c.repo_groups_data != '[]':
154 var pageInfo = $('#group_list_table').dataTable().api().page.info();
155 var repoGroupsCount = pageInfo.recordsDisplay;
156 var repoGroupsCountTotal = pageInfo.recordsTotal;
157 % endif
158
159 if (repoGroupsCount !== repoGroupsCountTotal) {
160 $('#match_count').text(reposCount + repoGroupsCount);
161 }
162 if (reposCount !== reposCountTotal) {
163 $('#match_container').show();
164 }
165 if ($('#q_filter').val() === '') {
166 $('#match_container').hide();
167 }
168 };
169
170 // update the counter when doing search
171 $('#repo_list_table, #group_list_table').on( 'search.dt', function (e,settings) {
172 getDatatableCount();
173 });
174
175 // filter, filter both grids
176 $('#q_filter').on( 'keyup', function () {
177
178 % if c.repo_groups_data != '[]':
179 var repo_group_api = $('#group_list_table').dataTable().api();
180 repo_group_api
181 .columns( 0 )
182 .search( this.value )
183 .draw();
184 % endif
185
186 % if c.repos_data != '[]':
187 var repo_api = $('#repo_list_table').dataTable().api();
188 repo_api
189 .columns( 0 )
190 .search( this.value )
191 .draw();
192 % endif
193
194 });
195
196 // refilter table if page load via back button
197 $("#q_filter").trigger('keyup');
198
199 139 });
200 140 </script>
201 141 </%def>
@@ -465,7 +465,7 b''
465 465 query: $.debounce(250, function(query) {
466 466 queryTargetRepo(this, query);
467 467 }),
468 formatResult: formatResult
468 formatResult: formatRepoResult
469 469 });
470 470
471 471 sourceRefSelect2.initRef();
General Comments 0
You need to be logged in to leave comments. Login now