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