##// END OF EJS Templates
quick-search: improve styling of search input and results.
dan -
r3443:6236d000 default
parent child Browse files
Show More
@@ -1,63 +1,69 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 def assert_and_get_main_filter_content(result):
23 23 repos = []
24 24 groups = []
25 25 commits = []
26 26 users = []
27 27 for data_item in result:
28 28 assert data_item['id']
29 29 assert data_item['value']
30 30 assert data_item['value_display']
31 31 assert data_item['url']
32 32
33 33 if data_item['type'] == 'search':
34 34 display_val = data_item['value_display']
35 assert 'Search for:' in display_val, display_val
35 if data_item['id'] == -1:
36 assert 'File search for:' in display_val, display_val
37 elif data_item['id'] == -2:
38 assert 'Commit search for:' in display_val, display_val
39 else:
40 assert False, 'No Proper ID returned {}'.format(data_item['id'])
41
36 42 elif data_item['type'] == 'repo':
37 43 repos.append(data_item)
38 44 elif data_item['type'] == 'repo_group':
39 45 groups.append(data_item)
40 46 elif data_item['type'] == 'user':
41 47 users.append(data_item)
42 48 elif data_item['type'] == 'commit':
43 49 commits.append(data_item)
44 50 else:
45 51 raise Exception('invalid type `%s`' % data_item['type'])
46 52
47 53 return repos, groups, users, commits
48 54
49 55
50 56 def assert_and_get_repo_list_content(result):
51 57 repos = []
52 58 for data in result:
53 59 for data_item in data['children']:
54 60 assert data_item['id']
55 61 assert data_item['text']
56 62 assert data_item['url']
57 63
58 64 if data_item['type'] == 'repo':
59 65 repos.append(data_item)
60 66 else:
61 67 raise Exception('invalid type %s' % data_item['type'])
62 68
63 69 return repos
@@ -1,596 +1,607 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import re
22 22 import logging
23 23 import collections
24 24
25 25 from pyramid.view import view_config
26 26
27 27 from rhodecode.apps._base import BaseAppView
28 28 from rhodecode.lib import helpers as h
29 29 from rhodecode.lib.auth import (
30 30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator,
31 31 CSRFRequired)
32 32 from rhodecode.lib.index import searcher_from_config
33 33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
34 34 from rhodecode.lib.ext_json import json
35 35 from rhodecode.model.db import (
36 36 func, true, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
37 37 from rhodecode.model.repo import RepoModel
38 38 from rhodecode.model.repo_group import RepoGroupModel
39 39 from rhodecode.model.scm import RepoGroupList, RepoList
40 40 from rhodecode.model.user import UserModel
41 41 from rhodecode.model.user_group import UserGroupModel
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 class HomeView(BaseAppView):
47 47
48 48 def load_default_context(self):
49 49 c = self._get_local_tmpl_context()
50 50 c.user = c.auth_user.get_instance()
51 51
52 52 return c
53 53
54 54 @LoginRequired()
55 55 @view_config(
56 56 route_name='user_autocomplete_data', request_method='GET',
57 57 renderer='json_ext', xhr=True)
58 58 def user_autocomplete_data(self):
59 59 self.load_default_context()
60 60 query = self.request.GET.get('query')
61 61 active = str2bool(self.request.GET.get('active') or True)
62 62 include_groups = str2bool(self.request.GET.get('user_groups'))
63 63 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
64 64 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
65 65
66 66 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
67 67 query, active, include_groups)
68 68
69 69 _users = UserModel().get_users(
70 70 name_contains=query, only_active=active)
71 71
72 72 def maybe_skip_default_user(usr):
73 73 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
74 74 return False
75 75 return True
76 76 _users = filter(maybe_skip_default_user, _users)
77 77
78 78 if include_groups:
79 79 # extend with user groups
80 80 _user_groups = UserGroupModel().get_user_groups(
81 81 name_contains=query, only_active=active,
82 82 expand_groups=expand_groups)
83 83 _users = _users + _user_groups
84 84
85 85 return {'suggestions': _users}
86 86
87 87 @LoginRequired()
88 88 @NotAnonymous()
89 89 @view_config(
90 90 route_name='user_group_autocomplete_data', request_method='GET',
91 91 renderer='json_ext', xhr=True)
92 92 def user_group_autocomplete_data(self):
93 93 self.load_default_context()
94 94 query = self.request.GET.get('query')
95 95 active = str2bool(self.request.GET.get('active') or True)
96 96 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
97 97
98 98 log.debug('generating user group list, query:%s, active:%s',
99 99 query, active)
100 100
101 101 _user_groups = UserGroupModel().get_user_groups(
102 102 name_contains=query, only_active=active,
103 103 expand_groups=expand_groups)
104 104 _user_groups = _user_groups
105 105
106 106 return {'suggestions': _user_groups}
107 107
108 108 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
109 109 org_query = name_contains
110 110 allowed_ids = self._rhodecode_user.repo_acl_ids(
111 111 ['repository.read', 'repository.write', 'repository.admin'],
112 112 cache=False, name_filter=name_contains) or [-1]
113 113
114 114 query = Repository.query()\
115 115 .order_by(func.length(Repository.repo_name))\
116 116 .order_by(Repository.repo_name)\
117 117 .filter(Repository.archived.isnot(true()))\
118 118 .filter(or_(
119 119 # generate multiple IN to fix limitation problems
120 120 *in_filter_generator(Repository.repo_id, allowed_ids)
121 121 ))
122 122
123 123 if repo_type:
124 124 query = query.filter(Repository.repo_type == repo_type)
125 125
126 126 if name_contains:
127 127 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
128 128 query = query.filter(
129 129 Repository.repo_name.ilike(ilike_expression))
130 130 query = query.limit(limit)
131 131
132 132 acl_iter = query
133 133
134 134 return [
135 135 {
136 136 'id': obj.repo_name,
137 137 'value': org_query,
138 138 'value_display': obj.repo_name,
139 139 'text': obj.repo_name,
140 140 'type': 'repo',
141 141 'repo_id': obj.repo_id,
142 142 'repo_type': obj.repo_type,
143 143 'private': obj.private,
144 144 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
145 145 }
146 146 for obj in acl_iter]
147 147
148 148 def _get_repo_group_list(self, name_contains=None, limit=20):
149 149 org_query = name_contains
150 150 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
151 151 ['group.read', 'group.write', 'group.admin'],
152 152 cache=False, name_filter=name_contains) or [-1]
153 153
154 154 query = RepoGroup.query()\
155 155 .order_by(func.length(RepoGroup.group_name))\
156 156 .order_by(RepoGroup.group_name) \
157 157 .filter(or_(
158 158 # generate multiple IN to fix limitation problems
159 159 *in_filter_generator(RepoGroup.group_id, allowed_ids)
160 160 ))
161 161
162 162 if name_contains:
163 163 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
164 164 query = query.filter(
165 165 RepoGroup.group_name.ilike(ilike_expression))
166 166 query = query.limit(limit)
167 167
168 168 acl_iter = query
169 169
170 170 return [
171 171 {
172 172 'id': obj.group_name,
173 173 'value': org_query,
174 174 'value_display': obj.group_name,
175 175 'text': obj.group_name,
176 176 'type': 'repo_group',
177 177 'repo_group_id': obj.group_id,
178 178 'url': h.route_path(
179 179 'repo_group_home', repo_group_name=obj.group_name)
180 180 }
181 181 for obj in acl_iter]
182 182
183 183 def _get_user_list(self, name_contains=None, limit=20):
184 184 org_query = name_contains
185 185 if not name_contains:
186 186 return []
187 187
188 188 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
189 189 if len(name_contains) != 1:
190 190 return []
191 191 name_contains = name_contains[0]
192 192
193 193 query = User.query()\
194 194 .order_by(func.length(User.username))\
195 195 .order_by(User.username) \
196 196 .filter(User.username != User.DEFAULT_USER)
197 197
198 198 if name_contains:
199 199 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
200 200 query = query.filter(
201 201 User.username.ilike(ilike_expression))
202 202 query = query.limit(limit)
203 203
204 204 acl_iter = query
205 205
206 206 return [
207 207 {
208 208 'id': obj.user_id,
209 209 'value': org_query,
210 210 'value_display': obj.username,
211 211 'type': 'user',
212 212 'icon_link': h.gravatar_url(obj.email, 30),
213 213 'url': h.route_path(
214 214 'user_profile', username=obj.username)
215 215 }
216 216 for obj in acl_iter]
217 217
218 218 def _get_user_groups_list(self, name_contains=None, limit=20):
219 219 org_query = name_contains
220 220 if not name_contains:
221 221 return []
222 222
223 223 name_contains = re.compile('(?:user_group:)(.+)').findall(name_contains)
224 224 if len(name_contains) != 1:
225 225 return []
226 226 name_contains = name_contains[0]
227 227
228 228 query = UserGroup.query()\
229 229 .order_by(func.length(UserGroup.users_group_name))\
230 230 .order_by(UserGroup.users_group_name)
231 231
232 232 if name_contains:
233 233 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
234 234 query = query.filter(
235 235 UserGroup.users_group_name.ilike(ilike_expression))
236 236 query = query.limit(limit)
237 237
238 238 acl_iter = query
239 239
240 240 return [
241 241 {
242 242 'id': obj.users_group_id,
243 243 'value': org_query,
244 244 'value_display': obj.users_group_name,
245 245 'type': 'user_group',
246 246 'url': h.route_path(
247 247 'user_group_profile', user_group_name=obj.users_group_name)
248 248 }
249 249 for obj in acl_iter]
250 250
251 251 def _get_hash_commit_list(self, auth_user, searcher, query):
252 252 org_query = query
253 253 if not query or len(query) < 3 or not searcher:
254 254 return []
255 255
256 256 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
257 257
258 258 if len(commit_hashes) != 1:
259 259 return []
260 260 commit_hash = commit_hashes[0]
261 261
262 262 result = searcher.search(
263 263 'commit_id:{}*'.format(commit_hash), 'commit', auth_user,
264 264 raise_on_exc=False)
265 265
266 return [
267 {
266 commits = []
267 for entry in result['results']:
268 repo_data = {
269 'repository_id': entry.get('repository_id'),
270 'repository_type': entry.get('repo_type'),
271 'repository_name': entry.get('repository'),
272 }
273
274 commit_entry = {
268 275 'id': entry['commit_id'],
269 276 'value': org_query,
270 'value_display': 'repo `{}` commit: {}'.format(
277 'value_display': '`{}` commit: {}'.format(
271 278 entry['repository'], entry['commit_id']),
272 279 'type': 'commit',
273 280 'repo': entry['repository'],
281 'repo_data': repo_data,
282
274 283 'url': h.route_path(
275 284 'repo_commit',
276 285 repo_name=entry['repository'], commit_id=entry['commit_id'])
277 286 }
278 for entry in result['results']]
287
288 commits.append(commit_entry)
289 return commits
279 290
280 291 @LoginRequired()
281 292 @view_config(
282 293 route_name='repo_list_data', request_method='GET',
283 294 renderer='json_ext', xhr=True)
284 295 def repo_list_data(self):
285 296 _ = self.request.translate
286 297 self.load_default_context()
287 298
288 299 query = self.request.GET.get('query')
289 300 repo_type = self.request.GET.get('repo_type')
290 301 log.debug('generating repo list, query:%s, repo_type:%s',
291 302 query, repo_type)
292 303
293 304 res = []
294 305 repos = self._get_repo_list(query, repo_type=repo_type)
295 306 if repos:
296 307 res.append({
297 308 'text': _('Repositories'),
298 309 'children': repos
299 310 })
300 311
301 312 data = {
302 313 'more': False,
303 314 'results': res
304 315 }
305 316 return data
306 317
307 318 @LoginRequired()
308 319 @view_config(
309 320 route_name='repo_group_list_data', request_method='GET',
310 321 renderer='json_ext', xhr=True)
311 322 def repo_group_list_data(self):
312 323 _ = self.request.translate
313 324 self.load_default_context()
314 325
315 326 query = self.request.GET.get('query')
316 327
317 328 log.debug('generating repo group list, query:%s',
318 329 query)
319 330
320 331 res = []
321 332 repo_groups = self._get_repo_group_list(query)
322 333 if repo_groups:
323 334 res.append({
324 335 'text': _('Repository Groups'),
325 336 'children': repo_groups
326 337 })
327 338
328 339 data = {
329 340 'more': False,
330 341 'results': res
331 342 }
332 343 return data
333 344
334 345 def _get_default_search_queries(self, search_context, searcher, query):
335 346 if not searcher:
336 347 return []
337 348
338 349 is_es_6 = searcher.is_es_6
339 350
340 351 queries = []
341 352 repo_group_name, repo_name, repo_context = None, None, None
342 353
343 354 # repo group context
344 355 if search_context.get('search_context[repo_group_name]'):
345 356 repo_group_name = search_context.get('search_context[repo_group_name]')
346 357 if search_context.get('search_context[repo_name]'):
347 358 repo_name = search_context.get('search_context[repo_name]')
348 359 repo_context = search_context.get('search_context[repo_view_type]')
349 360
350 361 if is_es_6 and repo_name:
351 362 # files
352 363 def query_modifier():
353 364 qry = query
354 365 return {'q': qry, 'type': 'content'}
355 366 label = u'File search for `{}` in this repository.'.format(query)
356 367 queries.append(
357 368 {
358 369 'id': -10,
359 370 'value': query,
360 371 'value_display': label,
361 372 'type': 'search',
362 373 'url': h.route_path('search_repo',
363 374 repo_name=repo_name,
364 375 _query=query_modifier())
365 376 }
366 377 )
367 378
368 379 # commits
369 380 def query_modifier():
370 381 qry = query
371 382 return {'q': qry, 'type': 'commit'}
372 383
373 384 label = u'Commit search for `{}` in this repository.'.format(query)
374 385 queries.append(
375 386 {
376 'id': -10,
387 'id': -20,
377 388 'value': query,
378 389 'value_display': label,
379 390 'type': 'search',
380 391 'url': h.route_path('search_repo',
381 392 repo_name=repo_name,
382 393 _query=query_modifier())
383 394 }
384 395 )
385 396
386 397 elif is_es_6 and repo_group_name:
387 398 # files
388 399 def query_modifier():
389 400 qry = query
390 401 return {'q': qry, 'type': 'content'}
391 402
392 403 label = u'File search for `{}` in this repository group'.format(query)
393 404 queries.append(
394 405 {
395 'id': -20,
406 'id': -30,
396 407 'value': query,
397 408 'value_display': label,
398 409 'type': 'search',
399 410 'url': h.route_path('search_repo_group',
400 411 repo_group_name=repo_group_name,
401 412 _query=query_modifier())
402 413 }
403 414 )
404 415
405 416 # commits
406 417 def query_modifier():
407 418 qry = query
408 419 return {'q': qry, 'type': 'commit'}
409 420
410 421 label = u'Commit search for `{}` in this repository group'.format(query)
411 422 queries.append(
412 423 {
413 'id': -20,
424 'id': -40,
414 425 'value': query,
415 426 'value_display': label,
416 427 'type': 'search',
417 428 'url': h.route_path('search_repo_group',
418 429 repo_group_name=repo_group_name,
419 430 _query=query_modifier())
420 431 }
421 432 )
422 433
423 434 if not queries:
424 435 queries.append(
425 436 {
426 437 'id': -1,
427 438 'value': query,
428 'value_display': u'Commit search for: `{}`'.format(query),
439 'value_display': u'File search for: `{}`'.format(query),
429 440 'type': 'search',
430 441 'url': h.route_path('search',
431 442 _query={'q': query, 'type': 'content'})
432 443 })
433 444 queries.append(
434 445 {
435 'id': -1,
446 'id': -2,
436 447 'value': query,
437 'value_display': u'File search for: `{}`'.format(query),
448 'value_display': u'Commit search for: `{}`'.format(query),
438 449 'type': 'search',
439 450 'url': h.route_path('search',
440 451 _query={'q': query, 'type': 'commit'})
441 452 })
442 453
443 454 return queries
444 455
445 456 @LoginRequired()
446 457 @view_config(
447 458 route_name='goto_switcher_data', request_method='GET',
448 459 renderer='json_ext', xhr=True)
449 460 def goto_switcher_data(self):
450 461 c = self.load_default_context()
451 462
452 463 _ = self.request.translate
453 464
454 465 query = self.request.GET.get('query')
455 466 log.debug('generating main filter data, query %s', query)
456 467
457 468 res = []
458 469 if not query:
459 470 return {'suggestions': res}
460 471
461 472 searcher = searcher_from_config(self.request.registry.settings)
462 473 for _q in self._get_default_search_queries(self.request.GET, searcher, query):
463 474 res.append(_q)
464 475
465 476 repo_group_id = safe_int(self.request.GET.get('search_context[repo_group_id]'))
466 477 if repo_group_id:
467 478 repo_group = RepoGroup.get(repo_group_id)
468 479 composed_hint = '{}/{}'.format(repo_group.group_name, query)
469 480 show_hint = not query.startswith(repo_group.group_name)
470 481 if repo_group and show_hint:
471 482 hint = u'Repository search inside: `{}`'.format(composed_hint)
472 483 res.append({
473 484 'id': -1,
474 485 'value': composed_hint,
475 486 'value_display': hint,
476 487 'type': 'hint',
477 488 'url': ""
478 489 })
479 490
480 491 repo_groups = self._get_repo_group_list(query)
481 492 for serialized_repo_group in repo_groups:
482 493 res.append(serialized_repo_group)
483 494
484 495 repos = self._get_repo_list(query)
485 496 for serialized_repo in repos:
486 497 res.append(serialized_repo)
487 498
488 499 # TODO(marcink): should all logged in users be allowed to search others?
489 500 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
490 501 if allowed_user_search:
491 502 users = self._get_user_list(query)
492 503 for serialized_user in users:
493 504 res.append(serialized_user)
494 505
495 506 user_groups = self._get_user_groups_list(query)
496 507 for serialized_user_group in user_groups:
497 508 res.append(serialized_user_group)
498 509
499 510 commits = self._get_hash_commit_list(c.auth_user, searcher, query)
500 511 if commits:
501 512 unique_repos = collections.OrderedDict()
502 513 for commit in commits:
503 514 repo_name = commit['repo']
504 515 unique_repos.setdefault(repo_name, []).append(commit)
505 516
506 517 for repo, commits in unique_repos.items():
507 518 for commit in commits:
508 519 res.append(commit)
509 520
510 521 return {'suggestions': res}
511 522
512 523 def _get_groups_and_repos(self, repo_group_id=None):
513 524 # repo groups groups
514 525 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
515 526 _perms = ['group.read', 'group.write', 'group.admin']
516 527 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
517 528 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
518 529 repo_group_list=repo_group_list_acl, admin=False)
519 530
520 531 # repositories
521 532 repo_list = Repository.get_all_repos(group_id=repo_group_id)
522 533 _perms = ['repository.read', 'repository.write', 'repository.admin']
523 534 repo_list_acl = RepoList(repo_list, perm_set=_perms)
524 535 repo_data = RepoModel().get_repos_as_dict(
525 536 repo_list=repo_list_acl, admin=False)
526 537
527 538 return repo_data, repo_group_data
528 539
529 540 @LoginRequired()
530 541 @view_config(
531 542 route_name='home', request_method='GET',
532 543 renderer='rhodecode:templates/index.mako')
533 544 def main_page(self):
534 545 c = self.load_default_context()
535 546 c.repo_group = None
536 547
537 548 repo_data, repo_group_data = self._get_groups_and_repos()
538 549 # json used to render the grids
539 550 c.repos_data = json.dumps(repo_data)
540 551 c.repo_groups_data = json.dumps(repo_group_data)
541 552
542 553 return self._get_template_context(c)
543 554
544 555 @LoginRequired()
545 556 @HasRepoGroupPermissionAnyDecorator(
546 557 'group.read', 'group.write', 'group.admin')
547 558 @view_config(
548 559 route_name='repo_group_home', request_method='GET',
549 560 renderer='rhodecode:templates/index_repo_group.mako')
550 561 @view_config(
551 562 route_name='repo_group_home_slash', request_method='GET',
552 563 renderer='rhodecode:templates/index_repo_group.mako')
553 564 def repo_group_main_page(self):
554 565 c = self.load_default_context()
555 566 c.repo_group = self.request.db_repo_group
556 567 repo_data, repo_group_data = self._get_groups_and_repos(
557 568 c.repo_group.group_id)
558 569
559 570 # json used to render the grids
560 571 c.repos_data = json.dumps(repo_data)
561 572 c.repo_groups_data = json.dumps(repo_group_data)
562 573
563 574 return self._get_template_context(c)
564 575
565 576 @LoginRequired()
566 577 @CSRFRequired()
567 578 @view_config(
568 579 route_name='markup_preview', request_method='POST',
569 580 renderer='string', xhr=True)
570 581 def markup_preview(self):
571 582 # Technically a CSRF token is not needed as no state changes with this
572 583 # call. However, as this is a POST is better to have it, so automated
573 584 # tools don't flag it as potential CSRF.
574 585 # Post is required because the payload could be bigger than the maximum
575 586 # allowed by GET.
576 587
577 588 text = self.request.POST.get('text')
578 589 renderer = self.request.POST.get('renderer') or 'rst'
579 590 if text:
580 591 return h.render(text, renderer=renderer, mentions=True)
581 592 return ''
582 593
583 594 @LoginRequired()
584 595 @CSRFRequired()
585 596 @view_config(
586 597 route_name='store_user_session_value', request_method='POST',
587 598 renderer='string', xhr=True)
588 599 def store_user_session_attr(self):
589 600 key = self.request.POST.get('key')
590 601 val = self.request.POST.get('val')
591 602
592 603 existing_value = self.request.session.get(key)
593 604 if existing_value != val:
594 605 self.request.session[key] = val
595 606
596 607 return 'stored:{}'.format(key)
@@ -1,684 +1,698 b''
1 1 // navigation.less
2 2 // For use in RhodeCode applications;
3 3 // see style guide documentation for guidelines.
4 4
5 5 // HEADER NAVIGATION
6 6
7 7 .horizontal-list {
8 8 float: right;
9 9 display: block;
10 10 margin: 0;
11 11 padding: 0;
12 12 -webkit-padding-start: 0;
13 13 text-align: left;
14 14 font-size: @navigation-fontsize;
15 15 color: @grey6;
16 16 z-index:10;
17 17
18 18 li {
19 19 line-height: 1em;
20 20 list-style-type: none;
21 21
22 22 a {
23 23 padding: 0 .5em;
24 24
25 25 &.menu_link_notifications {
26 26 .pill(7px,@rcblue);
27 27 display: inline;
28 28 margin: 0 7px 0 .7em;
29 29 font-size: @basefontsize;
30 30 color: white;
31 31
32 32 &.empty {
33 33 background-color: @grey4;
34 34 }
35 35
36 36 &:hover {
37 37 background-color: @rcdarkblue;
38 38 }
39 39 }
40 40 }
41 41 .pill_container {
42 42 margin: 1.25em 0px 0px 0px;
43 43 float: right;
44 44 }
45 45
46 46 &#quick_login_li {
47 47 &:hover {
48 48 color: @grey5;
49 49 }
50 50
51 51 a.menu_link_notifications {
52 52 color: white;
53 53 }
54 54
55 55 .user {
56 56 padding-bottom: 10px;
57 57 }
58 58
59 59 &.open {
60 60 .user {
61 61 border-bottom: 5px solid @rcblue;
62 62 }
63 63 }
64 64 }
65 65
66 66 &:before { content: none; }
67 67
68 68 &:last-child {
69 69 .menulabel {
70 70 padding-right: 0;
71 71 border-right: none;
72 72
73 73 .show_more {
74 74 padding-right: 0;
75 75 }
76 76 }
77 77
78 78 &> a {
79 79 border-bottom: none;
80 80 }
81 81 }
82 82
83 83 &.active {
84 84 border-bottom: 5px solid @rcblue;
85 85 }
86 86
87 87 &.open {
88 88
89 89 a {
90 90 color: white;
91 91 }
92 92 }
93 93
94 94 &:focus {
95 95 outline: none;
96 96 }
97 97
98 98 ul li {
99 99 display: block;
100 100
101 101 &:last-child> a {
102 102 border-bottom: none;
103 103 }
104 104
105 105 ul li:last-child a {
106 106 /* we don't expect more then 3 levels of submenu and the third
107 107 level can have different html structure */
108 108 border-bottom: none;
109 109 }
110 110 }
111 111 }
112 112
113 113 > li {
114 114 float: left;
115 115 display: block;
116 116 padding: 0;
117 117
118 118 > a,
119 119 &.has_select2 a {
120 120 display: block;
121 121 padding: 10px 0 2px;
122 122 }
123 123
124 124 .menulabel {
125 125 padding: 0 .5em;
126 126 line-height: 1em;
127 127 // for this specifically we do not use a variable
128 128 border-right: 1px solid @grey4;
129 129 }
130 130
131 131 .pr_notifications {
132 132 padding-left: .5em;
133 133 }
134 134
135 135 .pr_notifications + .menulabel {
136 136 display:inline;
137 137 padding-left: 0;
138 138 }
139 139
140 140 &:hover,
141 141 &.open,
142 142 &.active {
143 143 a {
144 144 color: @grey1;
145 145 }
146 146 }
147 147 }
148 148
149 149 pre {
150 150 margin: 0;
151 151 padding: 0;
152 152 }
153 153
154 154 .select2-container,
155 155 .menulink.childs {
156 156 position: relative;
157 157 }
158 158
159 159 #quick_login {
160 160
161 161 li a {
162 162 padding: .5em 0;
163 163 border-bottom: none;
164 164 color: @grey2;
165 165
166 166 &:hover { color: @grey1; }
167 167 }
168 168 }
169 169
170 170 #quick_login_link {
171 171 display: inline-block;
172 172
173 173 .gravatar {
174 174 border: 1px solid @grey5;
175 175 }
176 176
177 177 .gravatar-login {
178 178 height: 20px;
179 179 width: 20px;
180 180 margin: -8px 0;
181 181 padding: 0;
182 182 }
183 183
184 184 &:hover .user {
185 185 color: @grey6;
186 186 }
187 187 }
188 188 }
189 189 .header .horizontal-list {
190 190
191 191 li {
192 192
193 193 &#quick_login_li {
194 194 padding-left: .5em;
195 195
196 196 &:hover #quick_login_link {
197 197 color: inherit;
198 198 }
199 199
200 200 .menu_link_user {
201 201 padding: 0 2px;
202 202 }
203 203 }
204 204 list-style-type: none;
205 205 }
206 206
207 207 > li {
208 208
209 209 a {
210 210 padding: 18px 0 12px 0;
211 211 color: @nav-grey;
212 212
213 213 &.menu_link_notifications {
214 214 padding: 1px 8px;
215 215 }
216 216 }
217 217
218 218 &:hover,
219 219 &.open,
220 220 &.active {
221 221 .pill_container a {
222 222 // don't select text for the pill container, it has it' own
223 223 // hover behaviour
224 224 color: @nav-grey;
225 225 }
226 226 }
227 227
228 228 &:hover,
229 229 &.open,
230 230 &.active {
231 231 a {
232 232 color: @grey6;
233 233 }
234 234 }
235 235
236 236 .select2-dropdown-open a {
237 237 color: @grey6;
238 238 }
239 239
240 240 .repo-switcher {
241 241 padding-left: 0;
242 242
243 243 .menulabel {
244 244 padding-left: 0;
245 245 }
246 246 }
247 247 }
248 248
249 249 li ul li {
250 250 background-color:@grey2;
251 251
252 252 a {
253 253 padding: .5em 0;
254 254 border-bottom: @border-thickness solid @border-default-color;
255 255 color: @grey6;
256 256 }
257 257
258 258 &:last-child a, &.last a{
259 259 border-bottom: none;
260 260 }
261 261
262 262 &:hover {
263 263 background-color: @grey3;
264 264 }
265 265 }
266 266
267 267 .submenu {
268 268 margin-top: 5px;
269 269 }
270 270 }
271 271
272 272 // SUBMENUS
273 273 .navigation .submenu {
274 274 display: none;
275 275 }
276 276
277 277 .navigation li.open {
278 278 .submenu {
279 279 display: block;
280 280 }
281 281 }
282 282
283 283 .navigation li:last-child .submenu {
284 284 right: -20px;
285 285 left: auto;
286 286 }
287 287
288 288 .submenu {
289 289 position: absolute;
290 290 top: 100%;
291 291 left: 0;
292 292 min-width: 150px;
293 293 margin: 6px 0 0;
294 294 padding: 0;
295 295 text-align: left;
296 296 font-family: @text-light;
297 297 border-radius: @border-radius;
298 298 z-index: 20;
299 299
300 300 li {
301 301 display: block;
302 302 margin: 0;
303 303 padding: 0 .5em;
304 304 line-height: 1em;
305 305 color: @grey3;
306 306 background-color: @grey6;
307 307 list-style-type: none;
308 308
309 309 a {
310 310 display: block;
311 311 width: 100%;
312 312 padding: .5em 0;
313 313 border-right: none;
314 314 border-bottom: @border-thickness solid white;
315 315 color: @grey3;
316 316 }
317 317
318 318 ul {
319 319 display: none;
320 320 position: absolute;
321 321 top: 0;
322 322 right: 100%;
323 323 padding: 0;
324 324 z-index: 30;
325 325 }
326 326 &:hover {
327 327 background-color: @grey5;
328 328 -webkit-transition: background .3s;
329 329 -moz-transition: background .3s;
330 330 -o-transition: background .3s;
331 331 transition: background .3s;
332 332
333 333 ul {
334 334 display: block;
335 335 }
336 336 }
337 337 }
338 338
339 339 }
340 340
341 341
342 342
343 343
344 344 // repo dropdown
345 345 .quick_repo_menu {
346 346 width: 15px;
347 347 text-align: center;
348 348 position: relative;
349 349 cursor: pointer;
350 350
351 351 div {
352 352 overflow: visible !important;
353 353 }
354 354
355 355 &.sorting {
356 356 cursor: auto;
357 357 }
358 358
359 359 &:hover {
360 360 .menu_items_container {
361 361 position: absolute;
362 362 display: block;
363 363 }
364 364 .menu_items {
365 365 display: block;
366 366 }
367 367 }
368 368
369 369 i {
370 370 margin: 0;
371 371 color: @grey4;
372 372 }
373 373
374 374 .menu_items_container {
375 375 position: absolute;
376 376 top: 0;
377 377 left: 100%;
378 378 margin: 0;
379 379 padding: 0;
380 380 list-style: none;
381 381 background-color: @grey6;
382 382 z-index: 999;
383 383 text-align: left;
384 384
385 385 a {
386 386 color: @grey2;
387 387 }
388 388
389 389 ul.menu_items {
390 390 margin: 0;
391 391 padding: 0;
392 392 }
393 393
394 394 li {
395 395 margin: 0;
396 396 padding: 0;
397 397 line-height: 1em;
398 398 list-style-type: none;
399 399
400 400 a {
401 401 display: block;
402 402 height: 16px;
403 403 padding: 8px; //must add up to td height (28px)
404 404 width: 120px; // set width
405 405
406 406 &:hover {
407 407 background-color: @grey5;
408 408 -webkit-transition: background .3s;
409 409 -moz-transition: background .3s;
410 410 -o-transition: background .3s;
411 411 transition: background .3s;
412 412 }
413 413 }
414 414 }
415 415 }
416 416 }
417 417
418 418 // Header Repository Switcher
419 419 // Select2 Dropdown
420 420 #select2-drop.select2-drop.repo-switcher-dropdown {
421 421 width: auto !important;
422 422 margin-top: 5px;
423 423 padding: 1em 0;
424 424 text-align: left;
425 425 .border-radius-bottom(@border-radius);
426 426 border-color: transparent;
427 427 color: @grey6;
428 428 background-color: @grey2;
429 429
430 430 input {
431 431 min-width: 90%;
432 432 }
433 433
434 434 ul.select2-result-sub {
435 435
436 436 li {
437 437 line-height: 1em;
438 438
439 439 &:hover,
440 440 &.select2-highlighted {
441 441 background-color: @grey3;
442 442 }
443 443 }
444 444
445 445 &:before { content: none; }
446 446 }
447 447
448 448 ul.select2-results {
449 449 min-width: 200px;
450 450 margin: 0;
451 451 padding: 0;
452 452 list-style-type: none;
453 453 overflow-x: visible;
454 454 overflow-y: scroll;
455 455
456 456 li {
457 457 padding: 0 8px;
458 458 line-height: 1em;
459 459 color: @grey6;
460 460
461 461 &>.select2-result-label {
462 462 padding: 8px 0;
463 463 border-bottom: @border-thickness solid @grey3;
464 464 white-space: nowrap;
465 465 color: @grey5;
466 466 cursor: pointer;
467 467 }
468 468
469 469 &.select2-result-with-children {
470 470 margin: 0;
471 471 padding: 0;
472 472 }
473 473
474 474 &.select2-result-unselectable > .select2-result-label {
475 475 margin: 0 8px;
476 476 }
477 477
478 478 }
479 479 }
480 480
481 481 ul.select2-result-sub {
482 482 margin: 0;
483 483 padding: 0;
484 484
485 485 li {
486 486 display: block;
487 487 margin: 0;
488 488 border-right: none;
489 489 line-height: 1em;
490 490 font-family: @text-light;
491 491 color: @grey2;
492 492 list-style-type: none;
493 493
494 494 &:hover {
495 495 background-color: @grey3;
496 496 }
497 497 }
498 498 }
499 499 }
500 500
501 501
502 502 #context-bar {
503 503 display: block;
504 504 margin: 0 auto;
505 505 padding: 0 @header-padding;
506 506 background-color: @grey6;
507 507 border-bottom: @border-thickness solid @grey5;
508 508
509 509 .clear {
510 510 clear: both;
511 511 }
512 512 }
513 513
514 514 ul#context-pages {
515 515 li {
516 516 line-height: 1em;
517 517 list-style-type: none;
518 518
519 519 a {
520 520 color: @grey3;
521 521 }
522 522
523 523 &.active {
524 524 // special case, non-variable color
525 525 border-bottom: 4px solid @nav-grey;
526 526
527 527 a {
528 528 color: @grey1;
529 529 }
530 530 }
531 531 }
532 532 }
533 533
534 534 // PAGINATION
535 535
536 536 .pagination {
537 537 border: @border-thickness solid @rcblue;
538 538 color: @rcblue;
539 539
540 540 .current {
541 541 color: @grey4;
542 542 }
543 543 }
544 544
545 545 .dataTables_processing {
546 546 text-align: center;
547 547 font-size: 1.1em;
548 548 position: relative;
549 549 top: 95px;
550 550 }
551 551
552 552 .dataTables_paginate, .pagination-wh {
553 553 text-align: left;
554 554 display: inline-block;
555 555 border-left: 1px solid @rcblue;
556 556 float: none;
557 557 overflow: hidden;
558 558
559 559 .paginate_button, .pager_curpage,
560 560 .pager_link, .pg-previous, .pg-next, .pager_dotdot {
561 561 display: inline-block;
562 562 padding: @menupadding/4 @menupadding;
563 563 border: 1px solid @rcblue;
564 564 border-left: 0;
565 565 color: @rcblue;
566 566 cursor: pointer;
567 567 float: left;
568 568 }
569 569
570 570 .pager_curpage, .pager_dotdot,
571 571 .paginate_button.current, .paginate_button.disabled,
572 572 .disabled {
573 573 color: @grey3;
574 574 cursor: default;
575 575 }
576 576
577 577 .ellipsis {
578 578 display: inline-block;
579 579 text-align: left;
580 580 padding: @menupadding/4 @menupadding;
581 581 border: 1px solid @rcblue;
582 582 border-left: 0;
583 583 float: left;
584 584 }
585 585 }
586 586
587 587 // SIDEBAR
588 588
589 589 .sidebar {
590 590 .block-left;
591 591 clear: left;
592 592 max-width: @sidebar-width;
593 593 margin-right: @sidebarpadding;
594 594 padding-right: @sidebarpadding;
595 595 font-family: @text-regular;
596 596 color: @grey1;
597 597
598 598 &#graph_nodes {
599 599 clear:both;
600 600 width: auto;
601 601 margin-left: -100px;
602 602 padding: 0;
603 603 border: none;
604 604 }
605 605
606 606 .nav-pills {
607 607 margin: 0;
608 608 }
609 609
610 610 .nav {
611 611 list-style: none;
612 612 padding: 0;
613 613
614 614 li {
615 615 padding-bottom: @menupadding;
616 616 line-height: 1em;
617 617 color: @grey4;
618 618 list-style-type: none;
619 619
620 620 &.active a {
621 621 color: @grey2;
622 622 }
623 623
624 624 a {
625 625 color: @grey4;
626 626 }
627 627 }
628 628
629 629 }
630 630 }
631 631
632 632 .main_filter_help_box {
633 633 padding: 7px 7px;
634 634 border-top: 1px solid @grey4;
635 635 border-right: 1px solid @grey4;
636 636 border-bottom: 1px solid @grey4;
637 637 display: inline-block;
638 638 vertical-align: top;
639 margin-left: -7px;
640 background: @grey3;
639 background: inherit;
640 position: absolute;
641 right: 8px;
642 top: 9px;
641 643 }
642 644
643 645 .main_filter_input_box {
644 646 display: inline-block;
645 647 }
646 648
647 649 .main_filter_box {
648 650 margin: 9px 0 0 0;
649 651 }
650 652
651 653 #main_filter_help {
652 654 background: @grey3;
653 655 border: 1px solid black;
654 656 position: absolute;
655 white-space: pre-wrap;
657 white-space: pre;
656 658 z-index: 9999;
657 659 color: @nav-grey;
658 660 margin: 1px 7px;
659 padding: 0 2px;
661 padding: 0 10px;
660 662 }
661 663
662 664 .main_filter_input {
663 665 padding: 5px;
664 min-width: 220px;
666 min-width: 260px;
665 667 color: @nav-grey;
666 668 background: @grey3;
667 669 min-height: 18px;
670
671
672 &:active {
673 color: @grey2 !important;
674 background: white !important;
675 }
676 &:focus {
677 color: @grey2 !important;
678 background: white !important;
679 }
668 680 }
669 681
682
683
670 684 .main_filter_input::placeholder {
671 color: @nav-grey;
672 opacity: 1;
685 color: @nav-grey;
686 opacity: 1;
673 687 }
674 688
675 689 .notice-box {
676 690 display:block !important;
677 691 padding: 9px 0 !important;
678 692 }
679 693
680 694 .menulabel-notice {
681 695 border: 1px solid @color5;
682 696 padding:7px 10px;
683 697 color: @color5;
684 698 }
@@ -1,796 +1,815 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="root.mako"/>
3 3
4 4 <%include file="/ejs_templates/templates.html"/>
5 5
6 6 <div class="outerwrapper">
7 7 <!-- HEADER -->
8 8 <div class="header">
9 9 <div id="header-inner" class="wrapper">
10 10 <div id="logo">
11 11 <div class="logo-wrapper">
12 12 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
13 13 </div>
14 14 %if c.rhodecode_name:
15 15 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
16 16 %endif
17 17 </div>
18 18 <!-- MENU BAR NAV -->
19 19 ${self.menu_bar_nav()}
20 20 <!-- END MENU BAR NAV -->
21 21 </div>
22 22 </div>
23 23 ${self.menu_bar_subnav()}
24 24 <!-- END HEADER -->
25 25
26 26 <!-- CONTENT -->
27 27 <div id="content" class="wrapper">
28 28
29 29 <rhodecode-toast id="notifications"></rhodecode-toast>
30 30
31 31 <div class="main">
32 32 ${next.main()}
33 33 </div>
34 34 </div>
35 35 <!-- END CONTENT -->
36 36
37 37 </div>
38 38 <!-- FOOTER -->
39 39 <div id="footer">
40 40 <div id="footer-inner" class="title wrapper">
41 41 <div>
42 42 <p class="footer-link-right">
43 43 % if c.visual.show_version:
44 44 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
45 45 % endif
46 46 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
47 47 % if c.visual.rhodecode_support_url:
48 48 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
49 49 % endif
50 50 </p>
51 51 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
52 52 <p class="server-instance" style="display:${sid}">
53 53 ## display hidden instance ID if specially defined
54 54 % if c.rhodecode_instanceid:
55 55 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
56 56 % endif
57 57 </p>
58 58 </div>
59 59 </div>
60 60 </div>
61 61
62 62 <!-- END FOOTER -->
63 63
64 64 ### MAKO DEFS ###
65 65
66 66 <%def name="menu_bar_subnav()">
67 67 </%def>
68 68
69 69 <%def name="breadcrumbs(class_='breadcrumbs')">
70 70 <div class="${class_}">
71 71 ${self.breadcrumbs_links()}
72 72 </div>
73 73 </%def>
74 74
75 75 <%def name="admin_menu()">
76 76 <ul class="admin_menu submenu">
77 77 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
78 78 <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
79 79 <li><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
80 80 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
81 81 <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
82 82 <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
83 83 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
84 84 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
85 85 <li><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
86 86 <li class="last"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
87 87 </ul>
88 88 </%def>
89 89
90 90
91 91 <%def name="dt_info_panel(elements)">
92 92 <dl class="dl-horizontal">
93 93 %for dt, dd, title, show_items in elements:
94 94 <dt>${dt}:</dt>
95 95 <dd title="${h.tooltip(title)}">
96 96 %if callable(dd):
97 97 ## allow lazy evaluation of elements
98 98 ${dd()}
99 99 %else:
100 100 ${dd}
101 101 %endif
102 102 %if show_items:
103 103 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
104 104 %endif
105 105 </dd>
106 106
107 107 %if show_items:
108 108 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
109 109 %for item in show_items:
110 110 <dt></dt>
111 111 <dd>${item}</dd>
112 112 %endfor
113 113 </div>
114 114 %endif
115 115
116 116 %endfor
117 117 </dl>
118 118 </%def>
119 119
120 120
121 121 <%def name="gravatar(email, size=16)">
122 122 <%
123 123 if (size > 16):
124 124 gravatar_class = 'gravatar gravatar-large'
125 125 else:
126 126 gravatar_class = 'gravatar'
127 127 %>
128 128 <%doc>
129 129 TODO: johbo: For now we serve double size images to make it smooth
130 130 for retina. This is how it worked until now. Should be replaced
131 131 with a better solution at some point.
132 132 </%doc>
133 133 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
134 134 </%def>
135 135
136 136
137 137 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
138 138 <% email = h.email_or_none(contact) %>
139 139 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
140 140 ${self.gravatar(email, size)}
141 141 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
142 142 </div>
143 143 </%def>
144 144
145 145
146 146 ## admin menu used for people that have some admin resources
147 147 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
148 148 <ul class="submenu">
149 149 %if repositories:
150 150 <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
151 151 %endif
152 152 %if repository_groups:
153 153 <li class="local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
154 154 %endif
155 155 %if user_groups:
156 156 <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
157 157 %endif
158 158 </ul>
159 159 </%def>
160 160
161 161 <%def name="repo_page_title(repo_instance)">
162 162 <div class="title-content">
163 163 <div class="title-main">
164 164 ## SVN/HG/GIT icons
165 165 %if h.is_hg(repo_instance):
166 166 <i class="icon-hg"></i>
167 167 %endif
168 168 %if h.is_git(repo_instance):
169 169 <i class="icon-git"></i>
170 170 %endif
171 171 %if h.is_svn(repo_instance):
172 172 <i class="icon-svn"></i>
173 173 %endif
174 174
175 175 ## public/private
176 176 %if repo_instance.private:
177 177 <i class="icon-repo-private"></i>
178 178 %else:
179 179 <i class="icon-repo-public"></i>
180 180 %endif
181 181
182 182 ## repo name with group name
183 183 ${h.breadcrumb_repo_link(repo_instance)}
184 184
185 185 </div>
186 186
187 187 ## FORKED
188 188 %if repo_instance.fork:
189 189 <p>
190 190 <i class="icon-code-fork"></i> ${_('Fork of')}
191 191 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
192 192 </p>
193 193 %endif
194 194
195 195 ## IMPORTED FROM REMOTE
196 196 %if repo_instance.clone_uri:
197 197 <p>
198 198 <i class="icon-code-fork"></i> ${_('Clone from')}
199 199 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
200 200 </p>
201 201 %endif
202 202
203 203 ## LOCKING STATUS
204 204 %if repo_instance.locked[0]:
205 205 <p class="locking_locked">
206 206 <i class="icon-repo-lock"></i>
207 207 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
208 208 </p>
209 209 %elif repo_instance.enable_locking:
210 210 <p class="locking_unlocked">
211 211 <i class="icon-repo-unlock"></i>
212 212 ${_('Repository not locked. Pull repository to lock it.')}
213 213 </p>
214 214 %endif
215 215
216 216 </div>
217 217 </%def>
218 218
219 219 <%def name="repo_menu(active=None)">
220 220 <%
221 221 def is_active(selected):
222 222 if selected == active:
223 223 return "active"
224 224 %>
225 225
226 226 <!--- CONTEXT BAR -->
227 227 <div id="context-bar">
228 228 <div class="wrapper">
229 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>
233 233 <li class="${is_active('compare')}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
234 234 <li class="${is_active('search')}"><a class="menulink" href="${h.route_path('search_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Search')}</div></a></li>
235 235
236 236 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
237 237 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
238 238 <li class="${is_active('showpullrequest')}">
239 239 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
240 240 %if c.repository_pull_requests:
241 241 <span class="pr_notifications">${c.repository_pull_requests}</span>
242 242 %endif
243 243 <div class="menulabel">${_('Pull Requests')}</div>
244 244 </a>
245 245 </li>
246 246 %endif
247 247
248 248 <li class="${is_active('options')}">
249 249 <a class="menulink dropdown">
250 250 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
251 251 </a>
252 252 <ul class="submenu">
253 253 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
254 254 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Repository Settings')}</a></li>
255 255 %endif
256 256 %if c.rhodecode_db_repo.fork:
257 257 <li>
258 258 <a title="${h.tooltip(_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name))}"
259 259 href="${h.route_path('repo_compare',
260 260 repo_name=c.rhodecode_db_repo.fork.repo_name,
261 261 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
262 262 source_ref=c.rhodecode_db_repo.landing_rev[1],
263 263 target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
264 264 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
265 265 _query=dict(merge=1))}"
266 266 >
267 267 ${_('Compare fork')}
268 268 </a>
269 269 </li>
270 270 %endif
271 271
272 272 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
273 273 %if c.rhodecode_db_repo.locked[0]:
274 274 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
275 275 %else:
276 276 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
277 277 %endif
278 278 %endif
279 279 %if c.rhodecode_user.username != h.DEFAULT_USER:
280 280 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
281 281 <li><a href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">${_('Fork')}</a></li>
282 282 <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
283 283 %endif
284 284 %endif
285 285 </ul>
286 286 </li>
287 287 </ul>
288 288 </div>
289 289 <div class="clear"></div>
290 290 </div>
291 291 % if c.rhodecode_db_repo.archived:
292 292 <div class="alert alert-warning text-center">
293 293 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
294 294 </div>
295 295 % endif
296 296 <!--- END CONTEXT BAR -->
297 297
298 298 </%def>
299 299
300 300 <%def name="repo_group_page_title(repo_group_instance)">
301 301 <div class="title-content">
302 302 <div class="title-main">
303 303 ## Repository Group icon
304 304 <i class="icon-folder-close"></i>
305 305
306 306 ## repo name with group name
307 307 ${h.breadcrumb_repo_group_link(repo_group_instance)}
308 308 </div>
309 309
310 310 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
311 311 <div class="repo-group-desc">
312 312 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
313 313 </div>
314 314
315 315 </div>
316 316 </%def>
317 317
318 318 <%def name="repo_group_menu(active=None)">
319 319 <%
320 320 def is_active(selected):
321 321 if selected == active:
322 322 return "active"
323 323
324 324 is_admin = h.HasPermissionAny('hg.admin')('can create repos index page')
325 325
326 326 gr_name = c.repo_group.group_name if c.repo_group else None
327 327 # create repositories with write permission on group is set to true
328 328 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
329 329 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
330 330 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
331 331
332 332 %>
333 333
334 334 <!--- CONTEXT BAR -->
335 335 <div id="context-bar">
336 336 <div class="wrapper">
337 337 <ul id="context-pages" class="navigation horizontal-list">
338 338 <li class="${is_active('home')}"><a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a></li>
339 339 <li class="${is_active('search')}"><a class="menulink" href="${h.route_path('search_repo_group', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Search')}</div></a></li>
340 340
341 341 <li class="${is_active('options')}">
342 342 <a class="menulink dropdown">
343 343 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
344 344 </a>
345 345 <ul class="submenu">
346 346 %if is_admin or group_admin:
347 347 <li><a href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}">${_('Group Settings')}</a></li>
348 348 %endif
349 349 %if is_admin or group_admin or (group_write and create_on_write):
350 350 <li><a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('Add Repository')}</a></li>
351 351 %endif
352 352 %if is_admin or group_admin:
353 353 <li><a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'Add Parent Group')}</a></li>
354 354 %endif
355 355 </ul>
356 356 </li>
357 357 </ul>
358 358 </div>
359 359 <div class="clear"></div>
360 360 </div>
361 361
362 362 <!--- END CONTEXT BAR -->
363 363
364 364 </%def>
365 365
366 366
367 367 <%def name="usermenu(active=False)">
368 368 ## USER MENU
369 369 <li id="quick_login_li" class="${'active' if active else ''}">
370 370 % if c.rhodecode_user.username == h.DEFAULT_USER:
371 371 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
372 372 ${gravatar(c.rhodecode_user.email, 20)}
373 373 <span class="user">
374 374 <span>${_('Sign in')}</span>
375 375 </span>
376 376 </a>
377 377 % else:
378 378 ## logged in user
379 379 <a id="quick_login_link" class="menulink childs">
380 380 ${gravatar(c.rhodecode_user.email, 20)}
381 381 <span class="user">
382 382 <span class="menu_link_user">${c.rhodecode_user.username}</span>
383 383 <div class="show_more"></div>
384 384 </span>
385 385 </a>
386 386 ## subnav with menu for logged in user
387 387 <div class="user-menu submenu">
388 388 <div id="quick_login">
389 389 %if c.rhodecode_user.username != h.DEFAULT_USER:
390 390 <div class="">
391 391 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
392 392 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
393 393 <div class="email">${c.rhodecode_user.email}</div>
394 394 </div>
395 395 <div class="">
396 396 <ol class="links">
397 397 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
398 398 % if c.rhodecode_user.personal_repo_group:
399 399 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
400 400 % endif
401 401 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
402 402 ## bookmark-items
403 403 <li class="bookmark-items">
404 404 ${_('Bookmarks')}
405 405 <div class="pull-right">
406 406 <a href="${h.route_path('my_account_bookmarks')}">${_('Manage')}</a>
407 407 </div>
408 408 </li>
409 409 % if not c.bookmark_items:
410 410 <li>
411 411 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
412 412 </li>
413 413 % endif
414 414 % for item in c.bookmark_items:
415 415 <li>
416 416 % if item.repository:
417 417 <div>
418 418 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
419 419 <code>${item.position}</code>
420 420 % if item.repository.repo_type == 'hg':
421 421 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
422 422 % elif item.repository.repo_type == 'git':
423 423 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
424 424 % elif item.repository.repo_type == 'svn':
425 425 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
426 426 % endif
427 427 ${(item.title or h.shorter(item.repository.repo_name, 30))}
428 428 </a>
429 429 </div>
430 430 % elif item.repository_group:
431 431 <div>
432 432 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
433 433 <code>${item.position}</code>
434 434 <i class="icon-folder-close" title="${_('Repository group')}" style="font-size: 16px"></i>
435 435 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
436 436 </a>
437 437 </div>
438 438 % else:
439 439 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
440 440 <code>${item.position}</code>
441 441 ${item.title}
442 442 </a>
443 443 % endif
444 444 </li>
445 445 % endfor
446 446
447 447 <li class="logout">
448 448 ${h.secure_form(h.route_path('logout'), request=request)}
449 449 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
450 450 ${h.end_form()}
451 451 </li>
452 452 </ol>
453 453 </div>
454 454 %endif
455 455 </div>
456 456 </div>
457 457 ## unread counter
458 458 <div class="pill_container">
459 459 <a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a>
460 460 </div>
461 461 % endif
462 462 </li>
463 463 </%def>
464 464
465 465 <%def name="menu_items(active=None)">
466 466 <%
467 467 def is_active(selected):
468 468 if selected == active:
469 469 return "active"
470 470 return ""
471 471 %>
472 472
473 473 <ul id="quick" class="main_nav navigation horizontal-list">
474 474 ## notice box for important system messages
475 475 <li style="display: none">
476 476 <a class="notice-box" href="#openNotice" onclick="showNoticeBox(); return false">
477 477 <div class="menulabel-notice" >
478 478 0
479 479 </div>
480 480 </a>
481 481 </li>
482 482
483 483 ## Main filter
484 484 <li>
485 485 <div class="menulabel main_filter_box">
486 486 <div class="main_filter_input_box">
487 487 <input class="main_filter_input" id="main_filter" size="15" type="text" name="main_filter" placeholder="${_('search / go to...')}" value=""/>
488 488 </div>
489 489 <div class="main_filter_help_box">
490 490 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
491 491 </div>
492 492 </div>
493 493
494 494 <div id="main_filter_help" style="display: none">
495 Use '/' key to quickly access this field.
496 Enter name of repository, or repository group for quick search.
495 - Use '/' key to quickly access this field.
497 496
498 Prefix query to allow special search:
497 - Enter a name of repository, or repository group for quick search.
498
499 - Prefix query to allow special search:
499 500
500 user:admin, to search for usernames
501 user:admin, to search for usernames
501 502
502 user_group:devops, to search for user groups
503 user_group:devops, to search for user groups
503 504
504 commit:efced4, to search for commits
505
505 commit:efced4, to search for commits
506 506 </div>
507 507 </li>
508 508
509 509 ## ROOT MENU
510 510 %if c.rhodecode_user.username != h.DEFAULT_USER:
511 511 <li class="${is_active('journal')}">
512 512 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
513 513 <div class="menulabel">${_('Journal')}</div>
514 514 </a>
515 515 </li>
516 516 %else:
517 517 <li class="${is_active('journal')}">
518 518 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
519 519 <div class="menulabel">${_('Public journal')}</div>
520 520 </a>
521 521 </li>
522 522 %endif
523 523 <li class="${is_active('gists')}">
524 524 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
525 525 <div class="menulabel">${_('Gists')}</div>
526 526 </a>
527 527 </li>
528 528 <li class="${is_active('search')}">
529 529 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
530 530 <div class="menulabel">${_('Search')}</div>
531 531 </a>
532 532 </li>
533 533 % if h.HasPermissionAll('hg.admin')('access admin main page'):
534 534 <li class="${is_active('admin')}">
535 535 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
536 536 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
537 537 </a>
538 538 ${admin_menu()}
539 539 </li>
540 540 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
541 541 <li class="${is_active('admin')}">
542 542 <a class="menulink childs" title="${_('Delegated Admin settings')}">
543 543 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
544 544 </a>
545 545 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
546 546 c.rhodecode_user.repository_groups_admin,
547 547 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
548 548 </li>
549 549 % endif
550 550 ## render extra user menu
551 551 ${usermenu(active=(active=='my_account'))}
552 552
553 553 % if c.debug_style:
554 554 <li>
555 555 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
556 556 <div class="menulabel">${_('[Style]')}</div>
557 557 </a>
558 558 </li>
559 559 % endif
560 560 </ul>
561 561
562 562 <script type="text/javascript">
563 563 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
564 564
565 565 var formatRepoResult = function(result, container, query, escapeMarkup) {
566 566 return function(data, escapeMarkup) {
567 567 if (!data.repo_id){
568 568 return data.text; // optgroup text Repositories
569 569 }
570 570
571 571 var tmpl = '';
572 572 var repoType = data['repo_type'];
573 573 var repoName = data['text'];
574 574
575 575 if(data && data.type == 'repo'){
576 576 if(repoType === 'hg'){
577 577 tmpl += '<i class="icon-hg"></i> ';
578 578 }
579 579 else if(repoType === 'git'){
580 580 tmpl += '<i class="icon-git"></i> ';
581 581 }
582 582 else if(repoType === 'svn'){
583 583 tmpl += '<i class="icon-svn"></i> ';
584 584 }
585 585 if(data['private']){
586 586 tmpl += '<i class="icon-lock" ></i> ';
587 587 }
588 588 else if(visualShowPublicIcon){
589 589 tmpl += '<i class="icon-unlock-alt"></i> ';
590 590 }
591 591 }
592 592 tmpl += escapeMarkup(repoName);
593 593 return tmpl;
594 594
595 595 }(result, escapeMarkup);
596 596 };
597 597
598 598 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
599 599 return function(data, escapeMarkup) {
600 600 if (!data.repo_group_id){
601 601 return data.text; // optgroup text Repositories
602 602 }
603 603
604 604 var tmpl = '';
605 605 var repoGroupName = data['text'];
606 606
607 607 if(data){
608 608
609 609 tmpl += '<i class="icon-folder-close"></i> ';
610 610
611 611 }
612 612 tmpl += escapeMarkup(repoGroupName);
613 613 return tmpl;
614 614
615 615 }(result, escapeMarkup);
616 616 };
617 617
618 618
619 619 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
620 620
621 621 if (value.split(':').length === 2) {
622 622 value = value.split(':')[1]
623 623 }
624 624
625 625 var searchType = data['type'];
626 626 var valueDisplay = data['value_display'];
627 627
628 628 var escapeRegExChars = function (value) {
629 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
629 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
630 630 };
631 631 var pattern = '(' + escapeRegExChars(value) + ')';
632 632
633 var getRepoIcon = function(repo_type) {
634 if (repo_type === 'hg') {
635 return '<i class="icon-hg"></i> ';
636 }
637 else if (repo_type === 'git') {
638 return '<i class="icon-git"></i> ';
639 }
640 else if (repo_type === 'svn') {
641 return '<i class="icon-svn"></i> ';
642 }
643 return ''
644 };
645
633 646 // highlight match
634 647 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
635 648 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
636 649
637 650 var icon = '';
638 651
639 652 if (searchType === 'hint') {
640 653 icon += '<i class="icon-folder-close"></i> ';
641 654 }
655 // full text search
642 656 else if (searchType === 'search') {
643 657 icon += '<i class="icon-more"></i> ';
644 658 }
659 // repository
645 660 else if (searchType === 'repo') {
646 if (data['repo_type'] === 'hg') {
647 icon += '<i class="icon-hg"></i> ';
648 }
649 else if (data['repo_type'] === 'git') {
650 icon += '<i class="icon-git"></i> ';
651 }
652 else if (data['repo_type'] === 'svn') {
653 icon += '<i class="icon-svn"></i> ';
654 }
661
662 var repoIcon = getRepoIcon(data['repo_type']);
663 icon += repoIcon;
664
655 665 if (data['private']) {
656 666 icon += '<i class="icon-lock" ></i> ';
657 667 }
658 668 else if (visualShowPublicIcon) {
659 669 icon += '<i class="icon-unlock-alt"></i> ';
660 670 }
661 671 }
672 // repository groups
662 673 else if (searchType === 'repo_group') {
663 674 icon += '<i class="icon-folder-close"></i> ';
664 675 }
676 // user group
665 677 else if (searchType === 'user_group') {
666 678 icon += '<i class="icon-group"></i> ';
667 679 }
668 680 else if (searchType === 'user') {
669 681 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
670 682 }
683 // commit
671 684 else if (searchType === 'commit') {
672 icon += '<i class="icon-tag"></i>';
685 var repo_data = data['repo_data'];
686 var repoIcon = getRepoIcon(repo_data['repository_type']);
687 if (repoIcon) {
688 icon += repoIcon;
689 } else {
690 icon += '<i class="icon-tag"></i>';
691 }
673 692 }
674 693
675 694 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
676 695 return tmpl.format(icon, valueDisplay);
677 696 };
678 697
679 698 var handleSelect = function(element, suggestion) {
680 699 if (suggestion.type === "hint") {
681 700 // we skip action
682 701 $('#main_filter').focus();
683 702 } else {
684 703 window.location = suggestion['url'];
685 704 }
686 705 };
687 706 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
688 707 if (queryLowerCase.split(':').length === 2) {
689 708 queryLowerCase = queryLowerCase.split(':')[1]
690 709 }
691 710 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
692 711 };
693 712
694 713 $('#main_filter').autocomplete({
695 714 serviceUrl: pyroutes.url('goto_switcher_data'),
696 715 params: {"search_context": templateContext.search_context},
697 716 minChars:2,
698 717 maxHeight:400,
699 718 deferRequestBy: 300, //miliseconds
700 719 tabDisabled: true,
701 720 autoSelectFirst: true,
702 721 formatResult: autocompleteMainFilterFormatResult,
703 722 lookupFilter: autocompleteMainFilterResult,
704 723 onSelect: function (element, suggestion) {
705 724 handleSelect(element, suggestion);
706 725 return false;
707 726 },
708 727 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
709 728 if (jqXHR !== 'abort') {
710 729 alert("Error during search.\nError code: {0}".format(textStatus));
711 730 window.location = '';
712 731 }
713 732 }
714 733 });
715 734
716 735 showMainFilterBox = function () {
717 736 $('#main_filter_help').toggle();
718 }
737 };
719 738
720 739 </script>
721 740 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
722 741 </%def>
723 742
724 743 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
725 744 <div class="modal-dialog">
726 745 <div class="modal-content">
727 746 <div class="modal-header">
728 747 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
729 748 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
730 749 </div>
731 750 <div class="modal-body">
732 751 <div class="block-left">
733 752 <table class="keyboard-mappings">
734 753 <tbody>
735 754 <tr>
736 755 <th></th>
737 756 <th>${_('Site-wide shortcuts')}</th>
738 757 </tr>
739 758 <%
740 759 elems = [
741 760 ('/', 'Use quick search box'),
742 761 ('g h', 'Goto home page'),
743 762 ('g g', 'Goto my private gists page'),
744 763 ('g G', 'Goto my public gists page'),
745 764 ('g 0-9', 'Goto bookmarked items from 0-9'),
746 765 ('n r', 'New repository page'),
747 766 ('n g', 'New gist page'),
748 767 ]
749 768 %>
750 769 %for key, desc in elems:
751 770 <tr>
752 771 <td class="keys">
753 772 <span class="key tag">${key}</span>
754 773 </td>
755 774 <td>${desc}</td>
756 775 </tr>
757 776 %endfor
758 777 </tbody>
759 778 </table>
760 779 </div>
761 780 <div class="block-left">
762 781 <table class="keyboard-mappings">
763 782 <tbody>
764 783 <tr>
765 784 <th></th>
766 785 <th>${_('Repositories')}</th>
767 786 </tr>
768 787 <%
769 788 elems = [
770 789 ('g s', 'Goto summary page'),
771 790 ('g c', 'Goto changelog page'),
772 791 ('g f', 'Goto files page'),
773 792 ('g F', 'Goto files page with file search activated'),
774 793 ('g p', 'Goto pull requests page'),
775 794 ('g o', 'Goto repository settings'),
776 795 ('g O', 'Goto repository permissions settings'),
777 796 ]
778 797 %>
779 798 %for key, desc in elems:
780 799 <tr>
781 800 <td class="keys">
782 801 <span class="key tag">${key}</span>
783 802 </td>
784 803 <td>${desc}</td>
785 804 </tr>
786 805 %endfor
787 806 </tbody>
788 807 </table>
789 808 </div>
790 809 </div>
791 810 <div class="modal-footer">
792 811 </div>
793 812 </div><!-- /.modal-content -->
794 813 </div><!-- /.modal-dialog -->
795 814 </div><!-- /.modal -->
796 815
General Comments 0
You need to be logged in to leave comments. Login now