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