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