##// END OF EJS Templates
quick-search-bar: fixes #5634
marcink -
r4499:9f9ed4f5 stable
parent child Browse files
Show More
@@ -1,896 +1,897 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 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.httpexceptions import HTTPNotFound
26 26 from pyramid.view import view_config
27 27
28 28 from rhodecode.apps._base import BaseAppView, DataGridAppView
29 29 from rhodecode.lib import helpers as h
30 30 from rhodecode.lib.auth import (
31 31 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired,
32 32 HasRepoGroupPermissionAny, AuthUser)
33 33 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
34 34 from rhodecode.lib.index import searcher_from_config
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int, safe_str
36 36 from rhodecode.lib.vcs.nodes import FileNode
37 37 from rhodecode.model.db import (
38 38 func, true, or_, case, cast, in_filter_generator, String, Session,
39 39 Repository, RepoGroup, User, UserGroup, PullRequest)
40 40 from rhodecode.model.repo import RepoModel
41 41 from rhodecode.model.repo_group import RepoGroupModel
42 42 from rhodecode.model.user import UserModel
43 43 from rhodecode.model.user_group import UserGroupModel
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class HomeView(BaseAppView, DataGridAppView):
49 49
50 50 def load_default_context(self):
51 51 c = self._get_local_tmpl_context()
52 52 c.user = c.auth_user.get_instance()
53 53
54 54 return c
55 55
56 56 @LoginRequired()
57 57 @view_config(
58 58 route_name='user_autocomplete_data', request_method='GET',
59 59 renderer='json_ext', xhr=True)
60 60 def user_autocomplete_data(self):
61 61 self.load_default_context()
62 62 query = self.request.GET.get('query')
63 63 active = str2bool(self.request.GET.get('active') or True)
64 64 include_groups = str2bool(self.request.GET.get('user_groups'))
65 65 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
66 66 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
67 67
68 68 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
69 69 query, active, include_groups)
70 70
71 71 _users = UserModel().get_users(
72 72 name_contains=query, only_active=active)
73 73
74 74 def maybe_skip_default_user(usr):
75 75 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
76 76 return False
77 77 return True
78 78 _users = filter(maybe_skip_default_user, _users)
79 79
80 80 if include_groups:
81 81 # extend with user groups
82 82 _user_groups = UserGroupModel().get_user_groups(
83 83 name_contains=query, only_active=active,
84 84 expand_groups=expand_groups)
85 85 _users = _users + _user_groups
86 86
87 87 return {'suggestions': _users}
88 88
89 89 @LoginRequired()
90 90 @NotAnonymous()
91 91 @view_config(
92 92 route_name='user_group_autocomplete_data', request_method='GET',
93 93 renderer='json_ext', xhr=True)
94 94 def user_group_autocomplete_data(self):
95 95 self.load_default_context()
96 96 query = self.request.GET.get('query')
97 97 active = str2bool(self.request.GET.get('active') or True)
98 98 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
99 99
100 100 log.debug('generating user group list, query:%s, active:%s',
101 101 query, active)
102 102
103 103 _user_groups = UserGroupModel().get_user_groups(
104 104 name_contains=query, only_active=active,
105 105 expand_groups=expand_groups)
106 106 _user_groups = _user_groups
107 107
108 108 return {'suggestions': _user_groups}
109 109
110 110 def _get_repo_list(self, name_contains=None, repo_type=None, repo_group_name='', limit=20):
111 111 org_query = name_contains
112 112 allowed_ids = self._rhodecode_user.repo_acl_ids(
113 113 ['repository.read', 'repository.write', 'repository.admin'],
114 114 cache=True, name_filter=name_contains) or [-1]
115 115
116 116 query = Session().query(
117 117 Repository.repo_name,
118 118 Repository.repo_id,
119 119 Repository.repo_type,
120 120 Repository.private,
121 121 )\
122 122 .filter(Repository.archived.isnot(true()))\
123 123 .filter(or_(
124 124 # generate multiple IN to fix limitation problems
125 125 *in_filter_generator(Repository.repo_id, allowed_ids)
126 126 ))
127 127
128 128 query = query.order_by(case(
129 129 [
130 130 (Repository.repo_name.startswith(repo_group_name), repo_group_name+'/'),
131 131 ],
132 132 ))
133 133 query = query.order_by(func.length(Repository.repo_name))
134 134 query = query.order_by(Repository.repo_name)
135 135
136 136 if repo_type:
137 137 query = query.filter(Repository.repo_type == repo_type)
138 138
139 139 if name_contains:
140 140 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
141 141 query = query.filter(
142 142 Repository.repo_name.ilike(ilike_expression))
143 143 query = query.limit(limit)
144 144
145 145 acl_iter = query
146 146
147 147 return [
148 148 {
149 149 'id': obj.repo_name,
150 150 'value': org_query,
151 151 'value_display': obj.repo_name,
152 152 'text': obj.repo_name,
153 153 'type': 'repo',
154 154 'repo_id': obj.repo_id,
155 155 'repo_type': obj.repo_type,
156 156 'private': obj.private,
157 157 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
158 158 }
159 159 for obj in acl_iter]
160 160
161 161 def _get_repo_group_list(self, name_contains=None, repo_group_name='', limit=20):
162 162 org_query = name_contains
163 163 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
164 164 ['group.read', 'group.write', 'group.admin'],
165 165 cache=True, name_filter=name_contains) or [-1]
166 166
167 167 query = Session().query(
168 168 RepoGroup.group_id,
169 169 RepoGroup.group_name,
170 170 )\
171 171 .filter(or_(
172 172 # generate multiple IN to fix limitation problems
173 173 *in_filter_generator(RepoGroup.group_id, allowed_ids)
174 174 ))
175 175
176 176 query = query.order_by(case(
177 177 [
178 178 (RepoGroup.group_name.startswith(repo_group_name), repo_group_name+'/'),
179 179 ],
180 180 ))
181 181 query = query.order_by(func.length(RepoGroup.group_name))
182 182 query = query.order_by(RepoGroup.group_name)
183 183
184 184 if name_contains:
185 185 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
186 186 query = query.filter(
187 187 RepoGroup.group_name.ilike(ilike_expression))
188 188 query = query.limit(limit)
189 189
190 190 acl_iter = query
191 191
192 192 return [
193 193 {
194 194 'id': obj.group_name,
195 195 'value': org_query,
196 196 'value_display': obj.group_name,
197 197 'text': obj.group_name,
198 198 'type': 'repo_group',
199 199 'repo_group_id': obj.group_id,
200 200 'url': h.route_path(
201 201 'repo_group_home', repo_group_name=obj.group_name)
202 202 }
203 203 for obj in acl_iter]
204 204
205 205 def _get_user_list(self, name_contains=None, limit=20):
206 206 org_query = name_contains
207 207 if not name_contains:
208 208 return [], False
209 209
210 210 # TODO(marcink): should all logged in users be allowed to search others?
211 211 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
212 212 if not allowed_user_search:
213 213 return [], False
214 214
215 215 name_contains = re.compile('(?:user:[ ]?)(.+)').findall(name_contains)
216 216 if len(name_contains) != 1:
217 217 return [], False
218 218
219 219 name_contains = name_contains[0]
220 220
221 221 query = User.query()\
222 222 .order_by(func.length(User.username))\
223 223 .order_by(User.username) \
224 224 .filter(User.username != User.DEFAULT_USER)
225 225
226 226 if name_contains:
227 227 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
228 228 query = query.filter(
229 229 User.username.ilike(ilike_expression))
230 230 query = query.limit(limit)
231 231
232 232 acl_iter = query
233 233
234 234 return [
235 235 {
236 236 'id': obj.user_id,
237 237 'value': org_query,
238 238 'value_display': 'user: `{}`'.format(obj.username),
239 239 'type': 'user',
240 240 'icon_link': h.gravatar_url(obj.email, 30),
241 241 'url': h.route_path(
242 242 'user_profile', username=obj.username)
243 243 }
244 244 for obj in acl_iter], True
245 245
246 246 def _get_user_groups_list(self, name_contains=None, limit=20):
247 247 org_query = name_contains
248 248 if not name_contains:
249 249 return [], False
250 250
251 251 # TODO(marcink): should all logged in users be allowed to search others?
252 252 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
253 253 if not allowed_user_search:
254 254 return [], False
255 255
256 256 name_contains = re.compile('(?:user_group:[ ]?)(.+)').findall(name_contains)
257 257 if len(name_contains) != 1:
258 258 return [], False
259 259
260 260 name_contains = name_contains[0]
261 261
262 262 query = UserGroup.query()\
263 263 .order_by(func.length(UserGroup.users_group_name))\
264 264 .order_by(UserGroup.users_group_name)
265 265
266 266 if name_contains:
267 267 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
268 268 query = query.filter(
269 269 UserGroup.users_group_name.ilike(ilike_expression))
270 270 query = query.limit(limit)
271 271
272 272 acl_iter = query
273 273
274 274 return [
275 275 {
276 276 'id': obj.users_group_id,
277 277 'value': org_query,
278 278 'value_display': 'user_group: `{}`'.format(obj.users_group_name),
279 279 'type': 'user_group',
280 280 'url': h.route_path(
281 281 'user_group_profile', user_group_name=obj.users_group_name)
282 282 }
283 283 for obj in acl_iter], True
284 284
285 285 def _get_pull_request_list(self, name_contains=None, limit=20):
286 286 org_query = name_contains
287 287 if not name_contains:
288 288 return [], False
289 289
290 290 # TODO(marcink): should all logged in users be allowed to search others?
291 291 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
292 292 if not allowed_user_search:
293 293 return [], False
294 294
295 295 name_contains = re.compile('(?:pr:[ ]?)(.+)').findall(name_contains)
296 296 if len(name_contains) != 1:
297 297 return [], False
298 298
299 299 name_contains = name_contains[0]
300 300
301 301 allowed_ids = self._rhodecode_user.repo_acl_ids(
302 302 ['repository.read', 'repository.write', 'repository.admin'],
303 303 cache=True) or [-1]
304 304
305 305 query = Session().query(
306 306 PullRequest.pull_request_id,
307 307 PullRequest.title,
308 308 )
309 309 query = query.join(Repository, Repository.repo_id == PullRequest.target_repo_id)
310 310
311 311 query = query.filter(or_(
312 312 # generate multiple IN to fix limitation problems
313 313 *in_filter_generator(Repository.repo_id, allowed_ids)
314 314 ))
315 315
316 316 query = query.order_by(PullRequest.pull_request_id)
317 317
318 318 if name_contains:
319 319 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
320 320 query = query.filter(or_(
321 321 cast(PullRequest.pull_request_id, String).ilike(ilike_expression),
322 322 PullRequest.title.ilike(ilike_expression),
323 323 PullRequest.description.ilike(ilike_expression),
324 324 ))
325 325
326 326 query = query.limit(limit)
327 327
328 328 acl_iter = query
329 329
330 330 return [
331 331 {
332 332 'id': obj.pull_request_id,
333 333 'value': org_query,
334 'value_display': 'pull request: `!{} - {}`'.format(obj.pull_request_id, obj.title[:50]),
334 'value_display': 'pull request: `!{} - {}`'.format(
335 obj.pull_request_id, safe_str(obj.title[:50])),
335 336 'type': 'pull_request',
336 337 'url': h.route_path('pull_requests_global', pull_request_id=obj.pull_request_id)
337 338 }
338 339 for obj in acl_iter], True
339 340
340 341 def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None):
341 342 repo_name = repo_group_name = None
342 343 if repo:
343 344 repo_name = repo.repo_name
344 345 if repo_group:
345 346 repo_group_name = repo_group.group_name
346 347
347 348 org_query = query
348 349 if not query or len(query) < 3 or not searcher:
349 350 return [], False
350 351
351 352 commit_hashes = re.compile('(?:commit:[ ]?)([0-9a-f]{2,40})').findall(query)
352 353
353 354 if len(commit_hashes) != 1:
354 355 return [], False
355 356
356 357 commit_hash = commit_hashes[0]
357 358
358 359 result = searcher.search(
359 360 'commit_id:{}*'.format(commit_hash), 'commit', auth_user,
360 361 repo_name, repo_group_name, raise_on_exc=False)
361 362
362 363 commits = []
363 364 for entry in result['results']:
364 365 repo_data = {
365 366 'repository_id': entry.get('repository_id'),
366 367 'repository_type': entry.get('repo_type'),
367 368 'repository_name': entry.get('repository'),
368 369 }
369 370
370 371 commit_entry = {
371 372 'id': entry['commit_id'],
372 373 'value': org_query,
373 374 'value_display': '`{}` commit: {}'.format(
374 375 entry['repository'], entry['commit_id']),
375 376 'type': 'commit',
376 377 'repo': entry['repository'],
377 378 'repo_data': repo_data,
378 379
379 380 'url': h.route_path(
380 381 'repo_commit',
381 382 repo_name=entry['repository'], commit_id=entry['commit_id'])
382 383 }
383 384
384 385 commits.append(commit_entry)
385 386 return commits, True
386 387
387 388 def _get_path_list(self, auth_user, searcher, query, repo=None, repo_group=None):
388 389 repo_name = repo_group_name = None
389 390 if repo:
390 391 repo_name = repo.repo_name
391 392 if repo_group:
392 393 repo_group_name = repo_group.group_name
393 394
394 395 org_query = query
395 396 if not query or len(query) < 3 or not searcher:
396 397 return [], False
397 398
398 399 paths_re = re.compile('(?:file:[ ]?)(.+)').findall(query)
399 400 if len(paths_re) != 1:
400 401 return [], False
401 402
402 403 file_path = paths_re[0]
403 404
404 405 search_path = searcher.escape_specials(file_path)
405 406 result = searcher.search(
406 407 'file.raw:*{}*'.format(search_path), 'path', auth_user,
407 408 repo_name, repo_group_name, raise_on_exc=False)
408 409
409 410 files = []
410 411 for entry in result['results']:
411 412 repo_data = {
412 413 'repository_id': entry.get('repository_id'),
413 414 'repository_type': entry.get('repo_type'),
414 415 'repository_name': entry.get('repository'),
415 416 }
416 417
417 418 file_entry = {
418 419 'id': entry['commit_id'],
419 420 'value': org_query,
420 421 'value_display': '`{}` file: {}'.format(
421 422 entry['repository'], entry['file']),
422 423 'type': 'file',
423 424 'repo': entry['repository'],
424 425 'repo_data': repo_data,
425 426
426 427 'url': h.route_path(
427 428 'repo_files',
428 429 repo_name=entry['repository'], commit_id=entry['commit_id'],
429 430 f_path=entry['file'])
430 431 }
431 432
432 433 files.append(file_entry)
433 434 return files, True
434 435
435 436 @LoginRequired()
436 437 @view_config(
437 438 route_name='repo_list_data', request_method='GET',
438 439 renderer='json_ext', xhr=True)
439 440 def repo_list_data(self):
440 441 _ = self.request.translate
441 442 self.load_default_context()
442 443
443 444 query = self.request.GET.get('query')
444 445 repo_type = self.request.GET.get('repo_type')
445 446 log.debug('generating repo list, query:%s, repo_type:%s',
446 447 query, repo_type)
447 448
448 449 res = []
449 450 repos = self._get_repo_list(query, repo_type=repo_type)
450 451 if repos:
451 452 res.append({
452 453 'text': _('Repositories'),
453 454 'children': repos
454 455 })
455 456
456 457 data = {
457 458 'more': False,
458 459 'results': res
459 460 }
460 461 return data
461 462
462 463 @LoginRequired()
463 464 @view_config(
464 465 route_name='repo_group_list_data', request_method='GET',
465 466 renderer='json_ext', xhr=True)
466 467 def repo_group_list_data(self):
467 468 _ = self.request.translate
468 469 self.load_default_context()
469 470
470 471 query = self.request.GET.get('query')
471 472
472 473 log.debug('generating repo group list, query:%s',
473 474 query)
474 475
475 476 res = []
476 477 repo_groups = self._get_repo_group_list(query)
477 478 if repo_groups:
478 479 res.append({
479 480 'text': _('Repository Groups'),
480 481 'children': repo_groups
481 482 })
482 483
483 484 data = {
484 485 'more': False,
485 486 'results': res
486 487 }
487 488 return data
488 489
489 490 def _get_default_search_queries(self, search_context, searcher, query):
490 491 if not searcher:
491 492 return []
492 493
493 494 is_es_6 = searcher.is_es_6
494 495
495 496 queries = []
496 497 repo_group_name, repo_name, repo_context = None, None, None
497 498
498 499 # repo group context
499 500 if search_context.get('search_context[repo_group_name]'):
500 501 repo_group_name = search_context.get('search_context[repo_group_name]')
501 502 if search_context.get('search_context[repo_name]'):
502 503 repo_name = search_context.get('search_context[repo_name]')
503 504 repo_context = search_context.get('search_context[repo_view_type]')
504 505
505 506 if is_es_6 and repo_name:
506 507 # files
507 508 def query_modifier():
508 509 qry = query
509 510 return {'q': qry, 'type': 'content'}
510 511
511 512 label = u'File content search for `{}`'.format(h.escape(query))
512 513 file_qry = {
513 514 'id': -10,
514 515 'value': query,
515 516 'value_display': label,
516 517 'value_icon': '<i class="icon-code"></i>',
517 518 'type': 'search',
518 519 'subtype': 'repo',
519 520 'url': h.route_path('search_repo',
520 521 repo_name=repo_name,
521 522 _query=query_modifier())
522 523 }
523 524
524 525 # commits
525 526 def query_modifier():
526 527 qry = query
527 528 return {'q': qry, 'type': 'commit'}
528 529
529 530 label = u'Commit search for `{}`'.format(h.escape(query))
530 531 commit_qry = {
531 532 'id': -20,
532 533 'value': query,
533 534 'value_display': label,
534 535 'value_icon': '<i class="icon-history"></i>',
535 536 'type': 'search',
536 537 'subtype': 'repo',
537 538 'url': h.route_path('search_repo',
538 539 repo_name=repo_name,
539 540 _query=query_modifier())
540 541 }
541 542
542 543 if repo_context in ['commit', 'commits']:
543 544 queries.extend([commit_qry, file_qry])
544 545 elif repo_context in ['files', 'summary']:
545 546 queries.extend([file_qry, commit_qry])
546 547 else:
547 548 queries.extend([commit_qry, file_qry])
548 549
549 550 elif is_es_6 and repo_group_name:
550 551 # files
551 552 def query_modifier():
552 553 qry = query
553 554 return {'q': qry, 'type': 'content'}
554 555
555 556 label = u'File content search for `{}`'.format(query)
556 557 file_qry = {
557 558 'id': -30,
558 559 'value': query,
559 560 'value_display': label,
560 561 'value_icon': '<i class="icon-code"></i>',
561 562 'type': 'search',
562 563 'subtype': 'repo_group',
563 564 'url': h.route_path('search_repo_group',
564 565 repo_group_name=repo_group_name,
565 566 _query=query_modifier())
566 567 }
567 568
568 569 # commits
569 570 def query_modifier():
570 571 qry = query
571 572 return {'q': qry, 'type': 'commit'}
572 573
573 574 label = u'Commit search for `{}`'.format(query)
574 575 commit_qry = {
575 576 'id': -40,
576 577 'value': query,
577 578 'value_display': label,
578 579 'value_icon': '<i class="icon-history"></i>',
579 580 'type': 'search',
580 581 'subtype': 'repo_group',
581 582 'url': h.route_path('search_repo_group',
582 583 repo_group_name=repo_group_name,
583 584 _query=query_modifier())
584 585 }
585 586
586 587 if repo_context in ['commit', 'commits']:
587 588 queries.extend([commit_qry, file_qry])
588 589 elif repo_context in ['files', 'summary']:
589 590 queries.extend([file_qry, commit_qry])
590 591 else:
591 592 queries.extend([commit_qry, file_qry])
592 593
593 594 # Global, not scoped
594 595 if not queries:
595 596 queries.append(
596 597 {
597 598 'id': -1,
598 599 'value': query,
599 600 'value_display': u'File content search for: `{}`'.format(query),
600 601 'value_icon': '<i class="icon-code"></i>',
601 602 'type': 'search',
602 603 'subtype': 'global',
603 604 'url': h.route_path('search',
604 605 _query={'q': query, 'type': 'content'})
605 606 })
606 607 queries.append(
607 608 {
608 609 'id': -2,
609 610 'value': query,
610 611 'value_display': u'Commit search for: `{}`'.format(query),
611 612 'value_icon': '<i class="icon-history"></i>',
612 613 'type': 'search',
613 614 'subtype': 'global',
614 615 'url': h.route_path('search',
615 616 _query={'q': query, 'type': 'commit'})
616 617 })
617 618
618 619 return queries
619 620
620 621 @LoginRequired()
621 622 @view_config(
622 623 route_name='goto_switcher_data', request_method='GET',
623 624 renderer='json_ext', xhr=True)
624 625 def goto_switcher_data(self):
625 626 c = self.load_default_context()
626 627
627 628 _ = self.request.translate
628 629
629 630 query = self.request.GET.get('query')
630 631 log.debug('generating main filter data, query %s', query)
631 632
632 633 res = []
633 634 if not query:
634 635 return {'suggestions': res}
635 636
636 637 def no_match(name):
637 638 return {
638 639 'id': -1,
639 640 'value': "",
640 641 'value_display': name,
641 642 'type': 'text',
642 643 'url': ""
643 644 }
644 645 searcher = searcher_from_config(self.request.registry.settings)
645 646 has_specialized_search = False
646 647
647 648 # set repo context
648 649 repo = None
649 650 repo_id = safe_int(self.request.GET.get('search_context[repo_id]'))
650 651 if repo_id:
651 652 repo = Repository.get(repo_id)
652 653
653 654 # set group context
654 655 repo_group = None
655 656 repo_group_id = safe_int(self.request.GET.get('search_context[repo_group_id]'))
656 657 if repo_group_id:
657 658 repo_group = RepoGroup.get(repo_group_id)
658 659 prefix_match = False
659 660
660 661 # user: type search
661 662 if not prefix_match:
662 663 users, prefix_match = self._get_user_list(query)
663 664 if users:
664 665 has_specialized_search = True
665 666 for serialized_user in users:
666 667 res.append(serialized_user)
667 668 elif prefix_match:
668 669 has_specialized_search = True
669 670 res.append(no_match('No matching users found'))
670 671
671 672 # user_group: type search
672 673 if not prefix_match:
673 674 user_groups, prefix_match = self._get_user_groups_list(query)
674 675 if user_groups:
675 676 has_specialized_search = True
676 677 for serialized_user_group in user_groups:
677 678 res.append(serialized_user_group)
678 679 elif prefix_match:
679 680 has_specialized_search = True
680 681 res.append(no_match('No matching user groups found'))
681 682
682 683 # pr: type search
683 684 if not prefix_match:
684 685 pull_requests, prefix_match = self._get_pull_request_list(query)
685 686 if pull_requests:
686 687 has_specialized_search = True
687 688 for serialized_pull_request in pull_requests:
688 689 res.append(serialized_pull_request)
689 690 elif prefix_match:
690 691 has_specialized_search = True
691 692 res.append(no_match('No matching pull requests found'))
692 693
693 694 # FTS commit: type search
694 695 if not prefix_match:
695 696 commits, prefix_match = self._get_hash_commit_list(
696 697 c.auth_user, searcher, query, repo, repo_group)
697 698 if commits:
698 699 has_specialized_search = True
699 700 unique_repos = collections.OrderedDict()
700 701 for commit in commits:
701 702 repo_name = commit['repo']
702 703 unique_repos.setdefault(repo_name, []).append(commit)
703 704
704 705 for _repo, commits in unique_repos.items():
705 706 for commit in commits:
706 707 res.append(commit)
707 708 elif prefix_match:
708 709 has_specialized_search = True
709 710 res.append(no_match('No matching commits found'))
710 711
711 712 # FTS file: type search
712 713 if not prefix_match:
713 714 paths, prefix_match = self._get_path_list(
714 715 c.auth_user, searcher, query, repo, repo_group)
715 716 if paths:
716 717 has_specialized_search = True
717 718 unique_repos = collections.OrderedDict()
718 719 for path in paths:
719 720 repo_name = path['repo']
720 721 unique_repos.setdefault(repo_name, []).append(path)
721 722
722 723 for repo, paths in unique_repos.items():
723 724 for path in paths:
724 725 res.append(path)
725 726 elif prefix_match:
726 727 has_specialized_search = True
727 728 res.append(no_match('No matching files found'))
728 729
729 730 # main suggestions
730 731 if not has_specialized_search:
731 732 repo_group_name = ''
732 733 if repo_group:
733 734 repo_group_name = repo_group.group_name
734 735
735 736 for _q in self._get_default_search_queries(self.request.GET, searcher, query):
736 737 res.append(_q)
737 738
738 739 repo_groups = self._get_repo_group_list(query, repo_group_name=repo_group_name)
739 740 for serialized_repo_group in repo_groups:
740 741 res.append(serialized_repo_group)
741 742
742 743 repos = self._get_repo_list(query, repo_group_name=repo_group_name)
743 744 for serialized_repo in repos:
744 745 res.append(serialized_repo)
745 746
746 747 if not repos and not repo_groups:
747 748 res.append(no_match('No matches found'))
748 749
749 750 return {'suggestions': res}
750 751
751 752 @LoginRequired()
752 753 @view_config(
753 754 route_name='home', request_method='GET',
754 755 renderer='rhodecode:templates/index.mako')
755 756 def main_page(self):
756 757 c = self.load_default_context()
757 758 c.repo_group = None
758 759 return self._get_template_context(c)
759 760
760 761 def _main_page_repo_groups_data(self, repo_group_id):
761 762 column_map = {
762 763 'name': 'group_name_hash',
763 764 'desc': 'group_description',
764 765 'last_change': 'updated_on',
765 766 'owner': 'user_username',
766 767 }
767 768 draw, start, limit = self._extract_chunk(self.request)
768 769 search_q, order_by, order_dir = self._extract_ordering(
769 770 self.request, column_map=column_map)
770 771 return RepoGroupModel().get_repo_groups_data_table(
771 772 draw, start, limit,
772 773 search_q, order_by, order_dir,
773 774 self._rhodecode_user, repo_group_id)
774 775
775 776 def _main_page_repos_data(self, repo_group_id):
776 777 column_map = {
777 778 'name': 'repo_name',
778 779 'desc': 'description',
779 780 'last_change': 'updated_on',
780 781 'owner': 'user_username',
781 782 }
782 783 draw, start, limit = self._extract_chunk(self.request)
783 784 search_q, order_by, order_dir = self._extract_ordering(
784 785 self.request, column_map=column_map)
785 786 return RepoModel().get_repos_data_table(
786 787 draw, start, limit,
787 788 search_q, order_by, order_dir,
788 789 self._rhodecode_user, repo_group_id)
789 790
790 791 @LoginRequired()
791 792 @view_config(
792 793 route_name='main_page_repo_groups_data',
793 794 request_method='GET', renderer='json_ext', xhr=True)
794 795 def main_page_repo_groups_data(self):
795 796 self.load_default_context()
796 797 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
797 798
798 799 if repo_group_id:
799 800 group = RepoGroup.get_or_404(repo_group_id)
800 801 _perms = AuthUser.repo_group_read_perms
801 802 if not HasRepoGroupPermissionAny(*_perms)(
802 803 group.group_name, 'user is allowed to list repo group children'):
803 804 raise HTTPNotFound()
804 805
805 806 return self._main_page_repo_groups_data(repo_group_id)
806 807
807 808 @LoginRequired()
808 809 @view_config(
809 810 route_name='main_page_repos_data',
810 811 request_method='GET', renderer='json_ext', xhr=True)
811 812 def main_page_repos_data(self):
812 813 self.load_default_context()
813 814 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
814 815
815 816 if repo_group_id:
816 817 group = RepoGroup.get_or_404(repo_group_id)
817 818 _perms = AuthUser.repo_group_read_perms
818 819 if not HasRepoGroupPermissionAny(*_perms)(
819 820 group.group_name, 'user is allowed to list repo group children'):
820 821 raise HTTPNotFound()
821 822
822 823 return self._main_page_repos_data(repo_group_id)
823 824
824 825 @LoginRequired()
825 826 @HasRepoGroupPermissionAnyDecorator(*AuthUser.repo_group_read_perms)
826 827 @view_config(
827 828 route_name='repo_group_home', request_method='GET',
828 829 renderer='rhodecode:templates/index_repo_group.mako')
829 830 @view_config(
830 831 route_name='repo_group_home_slash', request_method='GET',
831 832 renderer='rhodecode:templates/index_repo_group.mako')
832 833 def repo_group_main_page(self):
833 834 c = self.load_default_context()
834 835 c.repo_group = self.request.db_repo_group
835 836 return self._get_template_context(c)
836 837
837 838 @LoginRequired()
838 839 @CSRFRequired()
839 840 @view_config(
840 841 route_name='markup_preview', request_method='POST',
841 842 renderer='string', xhr=True)
842 843 def markup_preview(self):
843 844 # Technically a CSRF token is not needed as no state changes with this
844 845 # call. However, as this is a POST is better to have it, so automated
845 846 # tools don't flag it as potential CSRF.
846 847 # Post is required because the payload could be bigger than the maximum
847 848 # allowed by GET.
848 849
849 850 text = self.request.POST.get('text')
850 851 renderer = self.request.POST.get('renderer') or 'rst'
851 852 if text:
852 853 return h.render(text, renderer=renderer, mentions=True)
853 854 return ''
854 855
855 856 @LoginRequired()
856 857 @CSRFRequired()
857 858 @view_config(
858 859 route_name='file_preview', request_method='POST',
859 860 renderer='string', xhr=True)
860 861 def file_preview(self):
861 862 # Technically a CSRF token is not needed as no state changes with this
862 863 # call. However, as this is a POST is better to have it, so automated
863 864 # tools don't flag it as potential CSRF.
864 865 # Post is required because the payload could be bigger than the maximum
865 866 # allowed by GET.
866 867
867 868 text = self.request.POST.get('text')
868 869 file_path = self.request.POST.get('file_path')
869 870
870 871 renderer = h.renderer_from_filename(file_path)
871 872
872 873 if renderer:
873 874 return h.render(text, renderer=renderer, mentions=True)
874 875 else:
875 876 self.load_default_context()
876 877 _render = self.request.get_partial_renderer(
877 878 'rhodecode:templates/files/file_content.mako')
878 879
879 880 lines = filenode_as_lines_tokens(FileNode(file_path, text))
880 881
881 882 return _render('render_lines', lines)
882 883
883 884 @LoginRequired()
884 885 @CSRFRequired()
885 886 @view_config(
886 887 route_name='store_user_session_value', request_method='POST',
887 888 renderer='string', xhr=True)
888 889 def store_user_session_attr(self):
889 890 key = self.request.POST.get('key')
890 891 val = self.request.POST.get('val')
891 892
892 893 existing_value = self.request.session.get(key)
893 894 if existing_value != val:
894 895 self.request.session[key] = val
895 896
896 897 return 'stored:{}:{}'.format(key, val)
@@ -1,1254 +1,1254 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%!
4 4 from rhodecode.lib import html_filters
5 5 %>
6 6
7 7 <%inherit file="root.mako"/>
8 8
9 9 <%include file="/ejs_templates/templates.html"/>
10 10
11 11 <div class="outerwrapper">
12 12 <!-- HEADER -->
13 13 <div class="header">
14 14 <div id="header-inner" class="wrapper">
15 15 <div id="logo">
16 16 <div class="logo-wrapper">
17 17 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
18 18 </div>
19 19 % if c.rhodecode_name:
20 20 <div class="branding">
21 21 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
22 22 </div>
23 23 % endif
24 24 </div>
25 25 <!-- MENU BAR NAV -->
26 26 ${self.menu_bar_nav()}
27 27 <!-- END MENU BAR NAV -->
28 28 </div>
29 29 </div>
30 30 ${self.menu_bar_subnav()}
31 31 <!-- END HEADER -->
32 32
33 33 <!-- CONTENT -->
34 34 <div id="content" class="wrapper">
35 35
36 36 <rhodecode-toast id="notifications"></rhodecode-toast>
37 37
38 38 <div class="main">
39 39 ${next.main()}
40 40 </div>
41 41
42 42 </div>
43 43 <!-- END CONTENT -->
44 44
45 45 </div>
46 46
47 47 <!-- FOOTER -->
48 48 <div id="footer">
49 49 <div id="footer-inner" class="title wrapper">
50 50 <div>
51 51 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
52 52
53 53 <p class="footer-link-right">
54 54 <a class="grey-link-action" href="${h.route_path('home', _query={'showrcid': 1})}">
55 55 RhodeCode
56 56 % if c.visual.show_version:
57 57 ${c.rhodecode_version}
58 58 % endif
59 59 ${c.rhodecode_edition}
60 60 </a> |
61 61
62 62 % if c.visual.rhodecode_support_url:
63 63 <a class="grey-link-action" href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a> |
64 64 <a class="grey-link-action" href="https://docs.rhodecode.com" target="_blank">${_('Documentation')}</a>
65 65 % endif
66 66
67 67 </p>
68 68
69 69 <p class="server-instance" style="display:${sid}">
70 70 ## display hidden instance ID if specially defined
71 71 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
72 72 % if c.rhodecode_instanceid:
73 73 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
74 74 % endif
75 75 </p>
76 76 </div>
77 77 </div>
78 78 </div>
79 79
80 80 <!-- END FOOTER -->
81 81
82 82 ### MAKO DEFS ###
83 83
84 84 <%def name="menu_bar_subnav()">
85 85 </%def>
86 86
87 87 <%def name="breadcrumbs(class_='breadcrumbs')">
88 88 <div class="${class_}">
89 89 ${self.breadcrumbs_links()}
90 90 </div>
91 91 </%def>
92 92
93 93 <%def name="admin_menu(active=None)">
94 94
95 95 <div id="context-bar">
96 96 <div class="wrapper">
97 97 <div class="title">
98 98 <div class="title-content">
99 99 <div class="title-main">
100 100 % if c.is_super_admin:
101 101 ${_('Super-admin Panel')}
102 102 % else:
103 103 ${_('Delegated Admin Panel')}
104 104 % endif
105 105 </div>
106 106 </div>
107 107 </div>
108 108
109 109 <ul id="context-pages" class="navigation horizontal-list">
110 110
111 111 ## super-admin case
112 112 % if c.is_super_admin:
113 113 <li class="${h.is_active('audit_logs', active)}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
114 114 <li class="${h.is_active('repositories', active)}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
115 115 <li class="${h.is_active('repository_groups', active)}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
116 116 <li class="${h.is_active('users', active)}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
117 117 <li class="${h.is_active('user_groups', active)}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
118 118 <li class="${h.is_active('permissions', active)}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
119 119 <li class="${h.is_active('authentication', active)}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
120 120 <li class="${h.is_active('integrations', active)}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
121 121 <li class="${h.is_active('defaults', active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
122 122 <li class="${h.is_active('settings', active)}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
123 123
124 124 ## delegated admin
125 125 % elif c.is_delegated_admin:
126 126 <%
127 127 repositories=c.auth_user.repositories_admin or c.can_create_repo
128 128 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
129 129 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
130 130 %>
131 131
132 132 %if repositories:
133 133 <li class="${h.is_active('repositories', active)} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
134 134 %endif
135 135 %if repository_groups:
136 136 <li class="${h.is_active('repository_groups', active)} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
137 137 %endif
138 138 %if user_groups:
139 139 <li class="${h.is_active('user_groups', active)} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
140 140 %endif
141 141 % endif
142 142 </ul>
143 143
144 144 </div>
145 145 <div class="clear"></div>
146 146 </div>
147 147 </%def>
148 148
149 149 <%def name="dt_info_panel(elements)">
150 150 <dl class="dl-horizontal">
151 151 %for dt, dd, title, show_items in elements:
152 152 <dt>${dt}:</dt>
153 153 <dd title="${h.tooltip(title)}">
154 154 %if callable(dd):
155 155 ## allow lazy evaluation of elements
156 156 ${dd()}
157 157 %else:
158 158 ${dd}
159 159 %endif
160 160 %if show_items:
161 161 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
162 162 %endif
163 163 </dd>
164 164
165 165 %if show_items:
166 166 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
167 167 %for item in show_items:
168 168 <dt></dt>
169 169 <dd>${item}</dd>
170 170 %endfor
171 171 </div>
172 172 %endif
173 173
174 174 %endfor
175 175 </dl>
176 176 </%def>
177 177
178 178 <%def name="tr_info_entry(element)">
179 179 <% key, val, title, show_items = element %>
180 180
181 181 <tr>
182 182 <td style="vertical-align: top">${key}</td>
183 183 <td title="${h.tooltip(title)}">
184 184 %if callable(val):
185 185 ## allow lazy evaluation of elements
186 186 ${val()}
187 187 %else:
188 188 ${val}
189 189 %endif
190 190 %if show_items:
191 191 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
192 192 % for item in show_items:
193 193 <dt></dt>
194 194 <dd>${item}</dd>
195 195 % endfor
196 196 </div>
197 197 %endif
198 198 </td>
199 199 <td style="vertical-align: top">
200 200 %if show_items:
201 201 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
202 202 %endif
203 203 </td>
204 204 </tr>
205 205
206 206 </%def>
207 207
208 208 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None, extra_class=None)">
209 209 <%
210 210 if size > 16:
211 211 gravatar_class = ['gravatar','gravatar-large']
212 212 else:
213 213 gravatar_class = ['gravatar']
214 214
215 215 data_hovercard_url = ''
216 216 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
217 217
218 218 if tooltip:
219 219 gravatar_class += ['tooltip-hovercard']
220 220 if extra_class:
221 221 gravatar_class += extra_class
222 222 if tooltip and user:
223 223 if user.username == h.DEFAULT_USER:
224 224 gravatar_class.pop(-1)
225 225 else:
226 226 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
227 227 gravatar_class = ' '.join(gravatar_class)
228 228
229 229 %>
230 230 <%doc>
231 231 TODO: johbo: For now we serve double size images to make it smooth
232 232 for retina. This is how it worked until now. Should be replaced
233 233 with a better solution at some point.
234 234 </%doc>
235 235
236 236 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
237 237 </%def>
238 238
239 239
240 240 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False, _class='rc-user')">
241 241 <%
242 242 email = h.email_or_none(contact)
243 243 rc_user = h.discover_user(contact)
244 244 %>
245 245
246 246 <div class="${_class}">
247 247 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
248 248 <span class="${('user user-disabled' if show_disabled else 'user')}">
249 249 ${h.link_to_user(rc_user or contact)}
250 250 </span>
251 251 </div>
252 252 </%def>
253 253
254 254
255 255 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
256 256 <%
257 257 if (size > 16):
258 258 gravatar_class = 'icon-user-group-alt'
259 259 else:
260 260 gravatar_class = 'icon-user-group-alt'
261 261
262 262 if tooltip:
263 263 gravatar_class += ' tooltip-hovercard'
264 264
265 265 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
266 266 %>
267 267 <%doc>
268 268 TODO: johbo: For now we serve double size images to make it smooth
269 269 for retina. This is how it worked until now. Should be replaced
270 270 with a better solution at some point.
271 271 </%doc>
272 272
273 273 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
274 274 </%def>
275 275
276 276 <%def name="repo_page_title(repo_instance)">
277 277 <div class="title-content repo-title">
278 278
279 279 <div class="title-main">
280 280 ## SVN/HG/GIT icons
281 281 %if h.is_hg(repo_instance):
282 282 <i class="icon-hg"></i>
283 283 %endif
284 284 %if h.is_git(repo_instance):
285 285 <i class="icon-git"></i>
286 286 %endif
287 287 %if h.is_svn(repo_instance):
288 288 <i class="icon-svn"></i>
289 289 %endif
290 290
291 291 ## public/private
292 292 %if repo_instance.private:
293 293 <i class="icon-repo-private"></i>
294 294 %else:
295 295 <i class="icon-repo-public"></i>
296 296 %endif
297 297
298 298 ## repo name with group name
299 299 ${h.breadcrumb_repo_link(repo_instance)}
300 300
301 301 ## Context Actions
302 302 <div class="pull-right">
303 303 %if c.rhodecode_user.username != h.DEFAULT_USER:
304 304 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
305 305
306 306 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
307 307 % if c.repository_is_user_following:
308 308 <i class="icon-eye-off"></i>${_('Unwatch')}
309 309 % else:
310 310 <i class="icon-eye"></i>${_('Watch')}
311 311 % endif
312 312
313 313 </a>
314 314 %else:
315 315 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
316 316 %endif
317 317 </div>
318 318
319 319 </div>
320 320
321 321 ## FORKED
322 322 %if repo_instance.fork:
323 323 <p class="discreet">
324 324 <i class="icon-code-fork"></i> ${_('Fork of')}
325 325 ${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))}
326 326 </p>
327 327 %endif
328 328
329 329 ## IMPORTED FROM REMOTE
330 330 %if repo_instance.clone_uri:
331 331 <p class="discreet">
332 332 <i class="icon-code-fork"></i> ${_('Clone from')}
333 333 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
334 334 </p>
335 335 %endif
336 336
337 337 ## LOCKING STATUS
338 338 %if repo_instance.locked[0]:
339 339 <p class="locking_locked discreet">
340 340 <i class="icon-repo-lock"></i>
341 341 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
342 342 </p>
343 343 %elif repo_instance.enable_locking:
344 344 <p class="locking_unlocked discreet">
345 345 <i class="icon-repo-unlock"></i>
346 346 ${_('Repository not locked. Pull repository to lock it.')}
347 347 </p>
348 348 %endif
349 349
350 350 </div>
351 351 </%def>
352 352
353 353 <%def name="repo_menu(active=None)">
354 354 <%
355 355 ## determine if we have "any" option available
356 356 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
357 357 has_actions = can_lock
358 358
359 359 %>
360 360 % if c.rhodecode_db_repo.archived:
361 361 <div class="alert alert-warning text-center">
362 362 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
363 363 </div>
364 364 % endif
365 365
366 366 <!--- REPO CONTEXT BAR -->
367 367 <div id="context-bar">
368 368 <div class="wrapper">
369 369
370 370 <div class="title">
371 371 ${self.repo_page_title(c.rhodecode_db_repo)}
372 372 </div>
373 373
374 374 <ul id="context-pages" class="navigation horizontal-list">
375 375 <li class="${h.is_active('summary', active)}"><a class="menulink" href="${h.route_path('repo_summary_explicit', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
376 376 <li class="${h.is_active('commits', active)}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
377 377 <li class="${h.is_active('files', active)}"><a class="menulink" href="${h.repo_files_by_ref_url(c.repo_name, c.rhodecode_db_repo.repo_type, f_path='', ref_name=c.rhodecode_db_repo.landing_ref_name, commit_id='tip', query={'at':c.rhodecode_db_repo.landing_ref_name})}"><div class="menulabel">${_('Files')}</div></a></li>
378 378 <li class="${h.is_active('compare', active)}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
379 379
380 380 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
381 381 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
382 382 <li class="${h.is_active('showpullrequest', active)}">
383 383 <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)}">
384 384 <div class="menulabel">
385 385 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
386 386 </div>
387 387 </a>
388 388 </li>
389 389 %endif
390 390
391 391 <li class="${h.is_active('artifacts', active)}">
392 392 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
393 393 <div class="menulabel">
394 394 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
395 395 </div>
396 396 </a>
397 397 </li>
398 398
399 399 %if not c.rhodecode_db_repo.archived and h.HasRepoPermissionAll('repository.admin')(c.repo_name):
400 400 <li class="${h.is_active('settings', active)}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
401 401 %endif
402 402
403 403 <li class="${h.is_active('options', active)}">
404 404 % if has_actions:
405 405 <a class="menulink dropdown">
406 406 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
407 407 </a>
408 408 <ul class="submenu">
409 409 %if can_lock:
410 410 %if c.rhodecode_db_repo.locked[0]:
411 411 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
412 412 %else:
413 413 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
414 414 %endif
415 415 %endif
416 416 </ul>
417 417 % endif
418 418 </li>
419 419
420 420 </ul>
421 421 </div>
422 422 <div class="clear"></div>
423 423 </div>
424 424
425 425 <!--- REPO END CONTEXT BAR -->
426 426
427 427 </%def>
428 428
429 429 <%def name="repo_group_page_title(repo_group_instance)">
430 430 <div class="title-content">
431 431 <div class="title-main">
432 432 ## Repository Group icon
433 433 <i class="icon-repo-group"></i>
434 434
435 435 ## repo name with group name
436 436 ${h.breadcrumb_repo_group_link(repo_group_instance)}
437 437 </div>
438 438
439 439 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
440 440 <div class="repo-group-desc discreet">
441 441 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
442 442 </div>
443 443
444 444 </div>
445 445 </%def>
446 446
447 447
448 448 <%def name="repo_group_menu(active=None)">
449 449 <%
450 450 gr_name = c.repo_group.group_name if c.repo_group else None
451 451 # create repositories with write permission on group is set to true
452 452 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
453 453
454 454 %>
455 455
456 456
457 457 <!--- REPO GROUP CONTEXT BAR -->
458 458 <div id="context-bar">
459 459 <div class="wrapper">
460 460 <div class="title">
461 461 ${self.repo_group_page_title(c.repo_group)}
462 462 </div>
463 463
464 464 <ul id="context-pages" class="navigation horizontal-list">
465 465 <li class="${h.is_active('home', active)}">
466 466 <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>
467 467 </li>
468 468 % if c.is_super_admin or group_admin:
469 469 <li class="${h.is_active('settings', active)}">
470 470 <a class="menulink" 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')}"><div class="menulabel">${_('Group Settings')}</div></a>
471 471 </li>
472 472 % endif
473 473
474 474 </ul>
475 475 </div>
476 476 <div class="clear"></div>
477 477 </div>
478 478
479 479 <!--- REPO GROUP CONTEXT BAR -->
480 480
481 481 </%def>
482 482
483 483
484 484 <%def name="usermenu(active=False)">
485 485 <%
486 486 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
487 487
488 488 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
489 489 # create repositories with write permission on group is set to true
490 490
491 491 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
492 492 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
493 493 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
494 494 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
495 495
496 496 can_create_repos = c.is_super_admin or c.can_create_repo
497 497 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
498 498
499 499 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
500 500 can_create_repo_groups_in_group = c.is_super_admin or group_admin
501 501 %>
502 502
503 503 % if not_anonymous:
504 504 <%
505 505 default_target_group = dict()
506 506 if c.rhodecode_user.personal_repo_group:
507 507 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
508 508 %>
509 509
510 510 ## create action
511 511 <li>
512 512 <a href="#create-actions" onclick="return false;" class="menulink childs">
513 513 <i class="icon-plus-circled"></i>
514 514 </a>
515 515
516 516 <div class="action-menu submenu">
517 517
518 518 <ol>
519 519 ## scope of within a repository
520 520 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
521 521 <li class="submenu-title">${_('This Repository')}</li>
522 522 <li>
523 523 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
524 524 </li>
525 525 % if can_fork:
526 526 <li>
527 527 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
528 528 </li>
529 529 % endif
530 530 % endif
531 531
532 532 ## scope of within repository groups
533 533 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
534 534 <li class="submenu-title">${_('This Repository Group')}</li>
535 535
536 536 % if can_create_repos_in_group:
537 537 <li>
538 538 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('New Repository')}</a>
539 539 </li>
540 540 % endif
541 541
542 542 % if can_create_repo_groups_in_group:
543 543 <li>
544 544 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'New Repository Group')}</a>
545 545 </li>
546 546 % endif
547 547 % endif
548 548
549 549 ## personal group
550 550 % if c.rhodecode_user.personal_repo_group:
551 551 <li class="submenu-title">Personal Group</li>
552 552
553 553 <li>
554 554 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
555 555 </li>
556 556
557 557 <li>
558 558 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
559 559 </li>
560 560 % endif
561 561
562 562 ## Global actions
563 563 <li class="submenu-title">RhodeCode</li>
564 564 % if can_create_repos:
565 565 <li>
566 566 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
567 567 </li>
568 568 % endif
569 569
570 570 % if can_create_repo_groups:
571 571 <li>
572 572 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
573 573 </li>
574 574 % endif
575 575
576 576 <li>
577 577 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
578 578 </li>
579 579
580 580 </ol>
581 581
582 582 </div>
583 583 </li>
584 584
585 585 ## notifications
586 586 <li>
587 587 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
588 588 ${c.unread_notifications}
589 589 </a>
590 590 </li>
591 591 % endif
592 592
593 593 ## USER MENU
594 594 <li id="quick_login_li" class="${'active' if active else ''}">
595 595 % if c.rhodecode_user.username == h.DEFAULT_USER:
596 596 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
597 597 ${gravatar(c.rhodecode_user.email, 20)}
598 598 <span class="user">
599 599 <span>${_('Sign in')}</span>
600 600 </span>
601 601 </a>
602 602 % else:
603 603 ## logged in user
604 604 <a id="quick_login_link" class="menulink childs">
605 605 ${gravatar(c.rhodecode_user.email, 20)}
606 606 <span class="user">
607 607 <span class="menu_link_user">${c.rhodecode_user.username}</span>
608 608 <div class="show_more"></div>
609 609 </span>
610 610 </a>
611 611 ## subnav with menu for logged in user
612 612 <div class="user-menu submenu">
613 613 <div id="quick_login">
614 614 %if c.rhodecode_user.username != h.DEFAULT_USER:
615 615 <div class="">
616 616 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
617 617 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
618 618 <div class="email">${c.rhodecode_user.email}</div>
619 619 </div>
620 620 <div class="">
621 621 <ol class="links">
622 622 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
623 623 % if c.rhodecode_user.personal_repo_group:
624 624 <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>
625 625 % endif
626 626 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
627 627
628 628 % if c.debug_style:
629 629 <li>
630 630 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
631 631 <div class="menulabel">${_('[Style]')}</div>
632 632 </a>
633 633 </li>
634 634 % endif
635 635
636 636 ## bookmark-items
637 637 <li class="bookmark-items">
638 638 ${_('Bookmarks')}
639 639 <div class="pull-right">
640 640 <a href="${h.route_path('my_account_bookmarks')}">
641 641
642 642 <i class="icon-cog"></i>
643 643 </a>
644 644 </div>
645 645 </li>
646 646 % if not c.bookmark_items:
647 647 <li>
648 648 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
649 649 </li>
650 650 % endif
651 651 % for item in c.bookmark_items:
652 652 <li>
653 653 % if item.repository:
654 654 <div>
655 655 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
656 656 <code>${item.position}</code>
657 657 % if item.repository.repo_type == 'hg':
658 658 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
659 659 % elif item.repository.repo_type == 'git':
660 660 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
661 661 % elif item.repository.repo_type == 'svn':
662 662 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
663 663 % endif
664 664 ${(item.title or h.shorter(item.repository.repo_name, 30))}
665 665 </a>
666 666 </div>
667 667 % elif item.repository_group:
668 668 <div>
669 669 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
670 670 <code>${item.position}</code>
671 671 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
672 672 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
673 673 </a>
674 674 </div>
675 675 % else:
676 676 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
677 677 <code>${item.position}</code>
678 678 ${item.title}
679 679 </a>
680 680 % endif
681 681 </li>
682 682 % endfor
683 683
684 684 <li class="logout">
685 685 ${h.secure_form(h.route_path('logout'), request=request)}
686 686 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
687 687 ${h.end_form()}
688 688 </li>
689 689 </ol>
690 690 </div>
691 691 %endif
692 692 </div>
693 693 </div>
694 694
695 695 % endif
696 696 </li>
697 697 </%def>
698 698
699 699 <%def name="menu_items(active=None)">
700 700 <%
701 701 notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
702 702 notice_display = 'none' if len(notice_messages) == 0 else ''
703 703 %>
704 704
705 705 <ul id="quick" class="main_nav navigation horizontal-list">
706 706 ## notice box for important system messages
707 707 <li style="display: ${notice_display}">
708 708 <a class="notice-box" href="#openNotice" onclick="$('.notice-messages-container').toggle(); return false">
709 709 <div class="menulabel-notice ${notice_level}" >
710 710 ${len(notice_messages)}
711 711 </div>
712 712 </a>
713 713 </li>
714 714 <div class="notice-messages-container" style="display: none">
715 715 <div class="notice-messages">
716 716 <table class="rctable">
717 717 % for notice in notice_messages:
718 718 <tr id="notice-message-${notice['msg_id']}" class="notice-message-${notice['level']}">
719 719 <td style="vertical-align: text-top; width: 20px">
720 720 <i class="tooltip icon-info notice-color-${notice['level']}" title="${notice['level']}"></i>
721 721 </td>
722 722 <td>
723 723 <span><i class="icon-plus-squared cursor-pointer" onclick="$('#notice-${notice['msg_id']}').toggle()"></i> </span>
724 724 ${notice['subject']}
725 725
726 726 <div id="notice-${notice['msg_id']}" style="display: none">
727 727 ${h.render(notice['body'], renderer='markdown')}
728 728 </div>
729 729 </td>
730 730 <td style="vertical-align: text-top; width: 35px;">
731 731 <a class="tooltip" title="${_('dismiss')}" href="#dismiss" onclick="dismissNotice(${notice['msg_id']});return false">
732 732 <i class="icon-remove icon-filled-red"></i>
733 733 </a>
734 734 </td>
735 735 </tr>
736 736
737 737 % endfor
738 738 </table>
739 739 </div>
740 740 </div>
741 741 ## Main filter
742 742 <li>
743 743 <div class="menulabel main_filter_box">
744 744 <div class="main_filter_input_box">
745 745 <ul class="searchItems">
746 746
747 747 <li class="searchTag searchTagIcon">
748 748 <i class="icon-search"></i>
749 749 </li>
750 750
751 751 % if c.template_context['search_context']['repo_id']:
752 752 <li class="searchTag searchTagFilter searchTagHidable" >
753 753 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
754 754 <span class="tag">
755 755 This repo
756 756 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
757 757 </span>
758 758 ##</a>
759 759 </li>
760 760 % elif c.template_context['search_context']['repo_group_id']:
761 761 <li class="searchTag searchTagFilter searchTagHidable">
762 762 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
763 763 <span class="tag">
764 764 This group
765 765 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
766 766 </span>
767 767 ##</a>
768 768 </li>
769 769 % endif
770 770
771 771 <li class="searchTagInput">
772 772 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
773 773 </li>
774 774 <li class="searchTag searchTagHelp">
775 775 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
776 776 </li>
777 777 </ul>
778 778 </div>
779 779 </div>
780 780
781 781 <div id="main_filter_help" style="display: none">
782 782 - Use '/' key to quickly access this field.
783 783
784 784 - Enter a name of repository, or repository group for quick search.
785 785
786 786 - Prefix query to allow special search:
787 787
788 user:admin, to search for usernames, always global
788 <strong>user:</strong>admin, to search for usernames, always global
789 789
790 user_group:devops, to search for user groups, always global
790 <strong>user_group:</strong>devops, to search for user groups, always global
791 791
792 pr:303, to search for pull request number, title, or description, always global
792 <strong>pr:</strong>303, to search for pull request number, title, or description, always global
793 793
794 commit:efced4, to search for commits, scoped to repositories or groups
794 <strong>commit:</strong>efced4, to search for commits, scoped to repositories or groups
795 795
796 file:models.py, to search for file paths, scoped to repositories or groups
796 <strong>file:</strong>models.py, to search for file paths, scoped to repositories or groups
797 797
798 798 % if c.template_context['search_context']['repo_id']:
799 799 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
800 800 % elif c.template_context['search_context']['repo_group_id']:
801 801 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
802 802 % else:
803 803 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
804 804 % endif
805 805 </div>
806 806 </li>
807 807
808 808 ## ROOT MENU
809 809 <li class="${h.is_active('home', active)}">
810 810 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
811 811 <div class="menulabel">${_('Home')}</div>
812 812 </a>
813 813 </li>
814 814
815 815 %if c.rhodecode_user.username != h.DEFAULT_USER:
816 816 <li class="${h.is_active('journal', active)}">
817 817 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
818 818 <div class="menulabel">${_('Journal')}</div>
819 819 </a>
820 820 </li>
821 821 %else:
822 822 <li class="${h.is_active('journal', active)}">
823 823 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
824 824 <div class="menulabel">${_('Public journal')}</div>
825 825 </a>
826 826 </li>
827 827 %endif
828 828
829 829 <li class="${h.is_active('gists', active)}">
830 830 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
831 831 <div class="menulabel">${_('Gists')}</div>
832 832 </a>
833 833 </li>
834 834
835 835 % if c.is_super_admin or c.is_delegated_admin:
836 836 <li class="${h.is_active('admin', active)}">
837 837 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
838 838 <div class="menulabel">${_('Admin')} </div>
839 839 </a>
840 840 </li>
841 841 % endif
842 842
843 843 ## render extra user menu
844 844 ${usermenu(active=(active=='my_account'))}
845 845
846 846 </ul>
847 847
848 848 <script type="text/javascript">
849 849 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
850 850
851 851 var formatRepoResult = function(result, container, query, escapeMarkup) {
852 852 return function(data, escapeMarkup) {
853 853 if (!data.repo_id){
854 854 return data.text; // optgroup text Repositories
855 855 }
856 856
857 857 var tmpl = '';
858 858 var repoType = data['repo_type'];
859 859 var repoName = data['text'];
860 860
861 861 if(data && data.type == 'repo'){
862 862 if(repoType === 'hg'){
863 863 tmpl += '<i class="icon-hg"></i> ';
864 864 }
865 865 else if(repoType === 'git'){
866 866 tmpl += '<i class="icon-git"></i> ';
867 867 }
868 868 else if(repoType === 'svn'){
869 869 tmpl += '<i class="icon-svn"></i> ';
870 870 }
871 871 if(data['private']){
872 872 tmpl += '<i class="icon-lock" ></i> ';
873 873 }
874 874 else if(visualShowPublicIcon){
875 875 tmpl += '<i class="icon-unlock-alt"></i> ';
876 876 }
877 877 }
878 878 tmpl += escapeMarkup(repoName);
879 879 return tmpl;
880 880
881 881 }(result, escapeMarkup);
882 882 };
883 883
884 884 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
885 885 return function(data, escapeMarkup) {
886 886 if (!data.repo_group_id){
887 887 return data.text; // optgroup text Repositories
888 888 }
889 889
890 890 var tmpl = '';
891 891 var repoGroupName = data['text'];
892 892
893 893 if(data){
894 894
895 895 tmpl += '<i class="icon-repo-group"></i> ';
896 896
897 897 }
898 898 tmpl += escapeMarkup(repoGroupName);
899 899 return tmpl;
900 900
901 901 }(result, escapeMarkup);
902 902 };
903 903
904 904 var escapeRegExChars = function (value) {
905 905 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
906 906 };
907 907
908 908 var getRepoIcon = function(repo_type) {
909 909 if (repo_type === 'hg') {
910 910 return '<i class="icon-hg"></i> ';
911 911 }
912 912 else if (repo_type === 'git') {
913 913 return '<i class="icon-git"></i> ';
914 914 }
915 915 else if (repo_type === 'svn') {
916 916 return '<i class="icon-svn"></i> ';
917 917 }
918 918 return ''
919 919 };
920 920
921 921 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
922 922
923 923 if (value.split(':').length === 2) {
924 924 value = value.split(':')[1]
925 925 }
926 926
927 927 var searchType = data['type'];
928 928 var searchSubType = data['subtype'];
929 929 var valueDisplay = data['value_display'];
930 930 var valueIcon = data['value_icon'];
931 931
932 932 var pattern = '(' + escapeRegExChars(value) + ')';
933 933
934 934 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
935 935
936 936 // highlight match
937 937 if (searchType != 'text') {
938 938 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
939 939 }
940 940
941 941 var icon = '';
942 942
943 943 if (searchType === 'hint') {
944 944 icon += '<i class="icon-repo-group"></i> ';
945 945 }
946 946 // full text search/hints
947 947 else if (searchType === 'search') {
948 948 if (valueIcon === undefined) {
949 949 icon += '<i class="icon-more"></i> ';
950 950 } else {
951 951 icon += valueIcon + ' ';
952 952 }
953 953
954 954 if (searchSubType !== undefined && searchSubType == 'repo') {
955 955 valueDisplay += '<div class="pull-right tag">repository</div>';
956 956 }
957 957 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
958 958 valueDisplay += '<div class="pull-right tag">repo group</div>';
959 959 }
960 960 }
961 961 // repository
962 962 else if (searchType === 'repo') {
963 963
964 964 var repoIcon = getRepoIcon(data['repo_type']);
965 965 icon += repoIcon;
966 966
967 967 if (data['private']) {
968 968 icon += '<i class="icon-lock" ></i> ';
969 969 }
970 970 else if (visualShowPublicIcon) {
971 971 icon += '<i class="icon-unlock-alt"></i> ';
972 972 }
973 973 }
974 974 // repository groups
975 975 else if (searchType === 'repo_group') {
976 976 icon += '<i class="icon-repo-group"></i> ';
977 977 }
978 978 // user group
979 979 else if (searchType === 'user_group') {
980 980 icon += '<i class="icon-group"></i> ';
981 981 }
982 982 // user
983 983 else if (searchType === 'user') {
984 984 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
985 985 }
986 986 // pull request
987 987 else if (searchType === 'pull_request') {
988 988 icon += '<i class="icon-merge"></i> ';
989 989 }
990 990 // commit
991 991 else if (searchType === 'commit') {
992 992 var repo_data = data['repo_data'];
993 993 var repoIcon = getRepoIcon(repo_data['repository_type']);
994 994 if (repoIcon) {
995 995 icon += repoIcon;
996 996 } else {
997 997 icon += '<i class="icon-tag"></i>';
998 998 }
999 999 }
1000 1000 // file
1001 1001 else if (searchType === 'file') {
1002 1002 var repo_data = data['repo_data'];
1003 1003 var repoIcon = getRepoIcon(repo_data['repository_type']);
1004 1004 if (repoIcon) {
1005 1005 icon += repoIcon;
1006 1006 } else {
1007 1007 icon += '<i class="icon-tag"></i>';
1008 1008 }
1009 1009 }
1010 1010 // generic text
1011 1011 else if (searchType === 'text') {
1012 1012 icon = '';
1013 1013 }
1014 1014
1015 1015 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
1016 1016 return tmpl.format(icon, valueDisplay);
1017 1017 };
1018 1018
1019 1019 var handleSelect = function(element, suggestion) {
1020 1020 if (suggestion.type === "hint") {
1021 1021 // we skip action
1022 1022 $('#main_filter').focus();
1023 1023 }
1024 1024 else if (suggestion.type === "text") {
1025 1025 // we skip action
1026 1026 $('#main_filter').focus();
1027 1027
1028 1028 } else {
1029 1029 window.location = suggestion['url'];
1030 1030 }
1031 1031 };
1032 1032
1033 1033 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
1034 1034 if (queryLowerCase.split(':').length === 2) {
1035 1035 queryLowerCase = queryLowerCase.split(':')[1]
1036 1036 }
1037 1037 if (suggestion.type === "text") {
1038 1038 // special case we don't want to "skip" display for
1039 1039 return true
1040 1040 }
1041 1041 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
1042 1042 };
1043 1043
1044 1044 var cleanContext = {
1045 1045 repo_view_type: null,
1046 1046
1047 1047 repo_id: null,
1048 1048 repo_name: "",
1049 1049
1050 1050 repo_group_id: null,
1051 1051 repo_group_name: null
1052 1052 };
1053 1053 var removeGoToFilter = function () {
1054 1054 $('.searchTagHidable').hide();
1055 1055 $('#main_filter').autocomplete(
1056 1056 'setOptions', {params:{search_context: cleanContext}});
1057 1057 };
1058 1058
1059 1059 $('#main_filter').autocomplete({
1060 1060 serviceUrl: pyroutes.url('goto_switcher_data'),
1061 1061 params: {
1062 1062 "search_context": templateContext.search_context
1063 1063 },
1064 1064 minChars:2,
1065 1065 maxHeight:400,
1066 1066 deferRequestBy: 300, //miliseconds
1067 1067 tabDisabled: true,
1068 1068 autoSelectFirst: false,
1069 1069 containerClass: 'autocomplete-qfilter-suggestions',
1070 1070 formatResult: autocompleteMainFilterFormatResult,
1071 1071 lookupFilter: autocompleteMainFilterResult,
1072 1072 onSelect: function (element, suggestion) {
1073 1073 handleSelect(element, suggestion);
1074 1074 return false;
1075 1075 },
1076 1076 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1077 1077 if (jqXHR !== 'abort') {
1078 1078 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
1079 1079 SwalNoAnimation.fire({
1080 1080 icon: 'error',
1081 1081 title: _gettext('Error during search operation'),
1082 1082 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
1083 1083 }).then(function(result) {
1084 1084 window.location.reload();
1085 1085 })
1086 1086 }
1087 1087 },
1088 1088 onSearchStart: function (params) {
1089 1089 $('.searchTag.searchTagIcon').html('<i class="icon-spin animate-spin"></i>')
1090 1090 },
1091 1091 onSearchComplete: function (query, suggestions) {
1092 1092 $('.searchTag.searchTagIcon').html('<i class="icon-search"></i>')
1093 1093 },
1094 1094 });
1095 1095
1096 1096 showMainFilterBox = function () {
1097 1097 $('#main_filter_help').toggle();
1098 1098 };
1099 1099
1100 1100 $('#main_filter').on('keydown.autocomplete', function (e) {
1101 1101
1102 1102 var BACKSPACE = 8;
1103 1103 var el = $(e.currentTarget);
1104 1104 if(e.which === BACKSPACE){
1105 1105 var inputVal = el.val();
1106 1106 if (inputVal === ""){
1107 1107 removeGoToFilter()
1108 1108 }
1109 1109 }
1110 1110 });
1111 1111
1112 1112 var dismissNotice = function(noticeId) {
1113 1113
1114 1114 var url = pyroutes.url('user_notice_dismiss',
1115 1115 {"user_id": templateContext.rhodecode_user.user_id});
1116 1116
1117 1117 var postData = {
1118 1118 'csrf_token': CSRF_TOKEN,
1119 1119 'notice_id': noticeId,
1120 1120 };
1121 1121
1122 1122 var success = function(response) {
1123 1123 $('#notice-message-' + noticeId).remove();
1124 1124 return false;
1125 1125 };
1126 1126 var failure = function(data, textStatus, xhr) {
1127 1127 alert("error processing request: " + textStatus);
1128 1128 return false;
1129 1129 };
1130 1130 ajaxPOST(url, postData, success, failure);
1131 1131 }
1132 1132
1133 1133 var hideLicenseWarning = function () {
1134 1134 var fingerprint = templateContext.session_attrs.license_fingerprint;
1135 1135 storeUserSessionAttr('rc_user_session_attr.hide_license_warning', fingerprint);
1136 1136 $('#notifications').hide();
1137 1137 }
1138 1138
1139 1139 var hideLicenseError = function () {
1140 1140 var fingerprint = templateContext.session_attrs.license_fingerprint;
1141 1141 storeUserSessionAttr('rc_user_session_attr.hide_license_error', fingerprint);
1142 1142 $('#notifications').hide();
1143 1143 }
1144 1144
1145 1145 </script>
1146 1146 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1147 1147 </%def>
1148 1148
1149 1149 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1150 1150 <div class="modal-dialog">
1151 1151 <div class="modal-content">
1152 1152 <div class="modal-header">
1153 1153 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1154 1154 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1155 1155 </div>
1156 1156 <div class="modal-body">
1157 1157 <div class="block-left">
1158 1158 <table class="keyboard-mappings">
1159 1159 <tbody>
1160 1160 <tr>
1161 1161 <th></th>
1162 1162 <th>${_('Site-wide shortcuts')}</th>
1163 1163 </tr>
1164 1164 <%
1165 1165 elems = [
1166 1166 ('/', 'Use quick search box'),
1167 1167 ('g h', 'Goto home page'),
1168 1168 ('g g', 'Goto my private gists page'),
1169 1169 ('g G', 'Goto my public gists page'),
1170 1170 ('g 0-9', 'Goto bookmarked items from 0-9'),
1171 1171 ('n r', 'New repository page'),
1172 1172 ('n g', 'New gist page'),
1173 1173 ]
1174 1174 %>
1175 1175 %for key, desc in elems:
1176 1176 <tr>
1177 1177 <td class="keys">
1178 1178 <span class="key tag">${key}</span>
1179 1179 </td>
1180 1180 <td>${desc}</td>
1181 1181 </tr>
1182 1182 %endfor
1183 1183 </tbody>
1184 1184 </table>
1185 1185 </div>
1186 1186 <div class="block-left">
1187 1187 <table class="keyboard-mappings">
1188 1188 <tbody>
1189 1189 <tr>
1190 1190 <th></th>
1191 1191 <th>${_('Repositories')}</th>
1192 1192 </tr>
1193 1193 <%
1194 1194 elems = [
1195 1195 ('g s', 'Goto summary page'),
1196 1196 ('g c', 'Goto changelog page'),
1197 1197 ('g f', 'Goto files page'),
1198 1198 ('g F', 'Goto files page with file search activated'),
1199 1199 ('g p', 'Goto pull requests page'),
1200 1200 ('g o', 'Goto repository settings'),
1201 1201 ('g O', 'Goto repository access permissions settings'),
1202 1202 ('t s', 'Toggle sidebar on some pages'),
1203 1203 ]
1204 1204 %>
1205 1205 %for key, desc in elems:
1206 1206 <tr>
1207 1207 <td class="keys">
1208 1208 <span class="key tag">${key}</span>
1209 1209 </td>
1210 1210 <td>${desc}</td>
1211 1211 </tr>
1212 1212 %endfor
1213 1213 </tbody>
1214 1214 </table>
1215 1215 </div>
1216 1216 </div>
1217 1217 <div class="modal-footer">
1218 1218 </div>
1219 1219 </div><!-- /.modal-content -->
1220 1220 </div><!-- /.modal-dialog -->
1221 1221 </div><!-- /.modal -->
1222 1222
1223 1223
1224 1224 <script type="text/javascript">
1225 1225 (function () {
1226 1226 "use sctrict";
1227 1227
1228 1228 var $sideBar = $('.right-sidebar');
1229 1229 var expanded = $sideBar.hasClass('right-sidebar-expanded');
1230 1230 var sidebarState = templateContext.session_attrs.sidebarState;
1231 1231 var sidebarEnabled = $('aside.right-sidebar').get(0);
1232 1232
1233 1233 if (sidebarState === 'expanded') {
1234 1234 expanded = true
1235 1235 } else if (sidebarState === 'collapsed') {
1236 1236 expanded = false
1237 1237 }
1238 1238 if (sidebarEnabled) {
1239 1239 // show sidebar since it's hidden on load
1240 1240 $('.right-sidebar').show();
1241 1241
1242 1242 // init based on set initial class, or if defined user session attrs
1243 1243 if (expanded) {
1244 1244 window.expandSidebar();
1245 1245 window.updateStickyHeader();
1246 1246
1247 1247 } else {
1248 1248 window.collapseSidebar();
1249 1249 window.updateStickyHeader();
1250 1250 }
1251 1251 }
1252 1252 })()
1253 1253
1254 1254 </script>
General Comments 0
You need to be logged in to leave comments. Login now