##// END OF EJS Templates
quick-serach: added option to also search user-groups.
marcink -
r2795:792316c3 default
parent child Browse files
Show More
@@ -1,376 +1,413 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator)
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator)
31 from rhodecode.lib.index import searcher_from_config
31 from rhodecode.lib.index import searcher_from_config
32 from rhodecode.lib.utils2 import safe_unicode, str2bool
32 from rhodecode.lib.utils2 import safe_unicode, str2bool
33 from rhodecode.lib.ext_json import json
33 from rhodecode.lib.ext_json import json
34 from rhodecode.model.db import (
34 from rhodecode.model.db import (
35 func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
35 func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
36 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.repo_group import RepoGroupModel
37 from rhodecode.model.repo_group import RepoGroupModel
38 from rhodecode.model.scm import RepoGroupList, RepoList
38 from rhodecode.model.scm import RepoGroupList, RepoList
39 from rhodecode.model.user import UserModel
39 from rhodecode.model.user import UserModel
40 from rhodecode.model.user_group import UserGroupModel
40 from rhodecode.model.user_group import UserGroupModel
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 class HomeView(BaseAppView):
45 class HomeView(BaseAppView):
46
46
47 def load_default_context(self):
47 def load_default_context(self):
48 c = self._get_local_tmpl_context()
48 c = self._get_local_tmpl_context()
49 c.user = c.auth_user.get_instance()
49 c.user = c.auth_user.get_instance()
50
50
51 return c
51 return c
52
52
53 @LoginRequired()
53 @LoginRequired()
54 @view_config(
54 @view_config(
55 route_name='user_autocomplete_data', request_method='GET',
55 route_name='user_autocomplete_data', request_method='GET',
56 renderer='json_ext', xhr=True)
56 renderer='json_ext', xhr=True)
57 def user_autocomplete_data(self):
57 def user_autocomplete_data(self):
58 self.load_default_context()
58 self.load_default_context()
59 query = self.request.GET.get('query')
59 query = self.request.GET.get('query')
60 active = str2bool(self.request.GET.get('active') or True)
60 active = str2bool(self.request.GET.get('active') or True)
61 include_groups = str2bool(self.request.GET.get('user_groups'))
61 include_groups = str2bool(self.request.GET.get('user_groups'))
62 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
62 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
63 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
63 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
64
64
65 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
65 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
66 query, active, include_groups)
66 query, active, include_groups)
67
67
68 _users = UserModel().get_users(
68 _users = UserModel().get_users(
69 name_contains=query, only_active=active)
69 name_contains=query, only_active=active)
70
70
71 def maybe_skip_default_user(usr):
71 def maybe_skip_default_user(usr):
72 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
72 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
73 return False
73 return False
74 return True
74 return True
75 _users = filter(maybe_skip_default_user, _users)
75 _users = filter(maybe_skip_default_user, _users)
76
76
77 if include_groups:
77 if include_groups:
78 # extend with user groups
78 # extend with user groups
79 _user_groups = UserGroupModel().get_user_groups(
79 _user_groups = UserGroupModel().get_user_groups(
80 name_contains=query, only_active=active,
80 name_contains=query, only_active=active,
81 expand_groups=expand_groups)
81 expand_groups=expand_groups)
82 _users = _users + _user_groups
82 _users = _users + _user_groups
83
83
84 return {'suggestions': _users}
84 return {'suggestions': _users}
85
85
86 @LoginRequired()
86 @LoginRequired()
87 @NotAnonymous()
87 @NotAnonymous()
88 @view_config(
88 @view_config(
89 route_name='user_group_autocomplete_data', request_method='GET',
89 route_name='user_group_autocomplete_data', request_method='GET',
90 renderer='json_ext', xhr=True)
90 renderer='json_ext', xhr=True)
91 def user_group_autocomplete_data(self):
91 def user_group_autocomplete_data(self):
92 self.load_default_context()
92 self.load_default_context()
93 query = self.request.GET.get('query')
93 query = self.request.GET.get('query')
94 active = str2bool(self.request.GET.get('active') or True)
94 active = str2bool(self.request.GET.get('active') or True)
95 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
95 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
96
96
97 log.debug('generating user group list, query:%s, active:%s',
97 log.debug('generating user group list, query:%s, active:%s',
98 query, active)
98 query, active)
99
99
100 _user_groups = UserGroupModel().get_user_groups(
100 _user_groups = UserGroupModel().get_user_groups(
101 name_contains=query, only_active=active,
101 name_contains=query, only_active=active,
102 expand_groups=expand_groups)
102 expand_groups=expand_groups)
103 _user_groups = _user_groups
103 _user_groups = _user_groups
104
104
105 return {'suggestions': _user_groups}
105 return {'suggestions': _user_groups}
106
106
107 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
107 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
108 org_query = name_contains
108 org_query = name_contains
109 allowed_ids = self._rhodecode_user.repo_acl_ids(
109 allowed_ids = self._rhodecode_user.repo_acl_ids(
110 ['repository.read', 'repository.write', 'repository.admin'],
110 ['repository.read', 'repository.write', 'repository.admin'],
111 cache=False, name_filter=name_contains) or [-1]
111 cache=False, name_filter=name_contains) or [-1]
112
112
113 query = Repository.query()\
113 query = Repository.query()\
114 .order_by(func.length(Repository.repo_name))\
114 .order_by(func.length(Repository.repo_name))\
115 .order_by(Repository.repo_name)\
115 .order_by(Repository.repo_name)\
116 .filter(or_(
116 .filter(or_(
117 # generate multiple IN to fix limitation problems
117 # generate multiple IN to fix limitation problems
118 *in_filter_generator(Repository.repo_id, allowed_ids)
118 *in_filter_generator(Repository.repo_id, allowed_ids)
119 ))
119 ))
120
120
121 if repo_type:
121 if repo_type:
122 query = query.filter(Repository.repo_type == repo_type)
122 query = query.filter(Repository.repo_type == repo_type)
123
123
124 if name_contains:
124 if name_contains:
125 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
125 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
126 query = query.filter(
126 query = query.filter(
127 Repository.repo_name.ilike(ilike_expression))
127 Repository.repo_name.ilike(ilike_expression))
128 query = query.limit(limit)
128 query = query.limit(limit)
129
129
130 acl_iter = query
130 acl_iter = query
131
131
132 return [
132 return [
133 {
133 {
134 'id': obj.repo_name,
134 'id': obj.repo_name,
135 'value': org_query,
135 'value': org_query,
136 'value_display': obj.repo_name,
136 'value_display': obj.repo_name,
137 'text': obj.repo_name,
137 'text': obj.repo_name,
138 'type': 'repo',
138 'type': 'repo',
139 'repo_id': obj.repo_id,
139 'repo_id': obj.repo_id,
140 'repo_type': obj.repo_type,
140 'repo_type': obj.repo_type,
141 'private': obj.private,
141 'private': obj.private,
142 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
142 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
143 }
143 }
144 for obj in acl_iter]
144 for obj in acl_iter]
145
145
146 def _get_repo_group_list(self, name_contains=None, limit=20):
146 def _get_repo_group_list(self, name_contains=None, limit=20):
147 org_query = name_contains
147 org_query = name_contains
148 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
148 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
149 ['group.read', 'group.write', 'group.admin'],
149 ['group.read', 'group.write', 'group.admin'],
150 cache=False, name_filter=name_contains) or [-1]
150 cache=False, name_filter=name_contains) or [-1]
151
151
152 query = RepoGroup.query()\
152 query = RepoGroup.query()\
153 .order_by(func.length(RepoGroup.group_name))\
153 .order_by(func.length(RepoGroup.group_name))\
154 .order_by(RepoGroup.group_name) \
154 .order_by(RepoGroup.group_name) \
155 .filter(or_(
155 .filter(or_(
156 # generate multiple IN to fix limitation problems
156 # generate multiple IN to fix limitation problems
157 *in_filter_generator(RepoGroup.group_id, allowed_ids)
157 *in_filter_generator(RepoGroup.group_id, allowed_ids)
158 ))
158 ))
159
159
160 if name_contains:
160 if name_contains:
161 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
161 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
162 query = query.filter(
162 query = query.filter(
163 RepoGroup.group_name.ilike(ilike_expression))
163 RepoGroup.group_name.ilike(ilike_expression))
164 query = query.limit(limit)
164 query = query.limit(limit)
165
165
166 acl_iter = query
166 acl_iter = query
167
167
168 return [
168 return [
169 {
169 {
170 'id': obj.group_name,
170 'id': obj.group_name,
171 'value': org_query,
171 'value': org_query,
172 'value_display': obj.group_name,
172 'value_display': obj.group_name,
173 'type': 'repo_group',
173 'type': 'repo_group',
174 'url': h.route_path(
174 'url': h.route_path(
175 'repo_group_home', repo_group_name=obj.group_name)
175 'repo_group_home', repo_group_name=obj.group_name)
176 }
176 }
177 for obj in acl_iter]
177 for obj in acl_iter]
178
178
179 def _get_user_list(self, name_contains=None, limit=20):
179 def _get_user_list(self, name_contains=None, limit=20):
180 org_query = name_contains
180 org_query = name_contains
181 if not name_contains:
181 if not name_contains:
182 return []
182 return []
183
183
184 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
184 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
185 if len(name_contains) != 1:
185 if len(name_contains) != 1:
186 return []
186 return []
187 name_contains = name_contains[0]
187 name_contains = name_contains[0]
188
188
189 query = User.query()\
189 query = User.query()\
190 .order_by(func.length(User.username))\
190 .order_by(func.length(User.username))\
191 .order_by(User.username) \
191 .order_by(User.username) \
192 .filter(User.username != User.DEFAULT_USER)
192 .filter(User.username != User.DEFAULT_USER)
193
193
194 if name_contains:
194 if name_contains:
195 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
195 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
196 query = query.filter(
196 query = query.filter(
197 User.username.ilike(ilike_expression))
197 User.username.ilike(ilike_expression))
198 query = query.limit(limit)
198 query = query.limit(limit)
199
199
200 acl_iter = query
200 acl_iter = query
201
201
202 return [
202 return [
203 {
203 {
204 'id': obj.user_id,
204 'id': obj.user_id,
205 'value': org_query,
205 'value': org_query,
206 'value_display': obj.username,
206 'value_display': obj.username,
207 'type': 'user',
207 'type': 'user',
208 'icon_link': h.gravatar_url(obj.email, 30),
208 'icon_link': h.gravatar_url(obj.email, 30),
209 'url': h.route_path(
209 'url': h.route_path(
210 'user_profile', username=obj.username)
210 'user_profile', username=obj.username)
211 }
211 }
212 for obj in acl_iter]
212 for obj in acl_iter]
213
213
214 def _get_user_groups_list(self, name_contains=None, limit=20):
215 org_query = name_contains
216 if not name_contains:
217 return []
218
219 name_contains = re.compile('(?:user_group:)(.+)').findall(name_contains)
220 if len(name_contains) != 1:
221 return []
222 name_contains = name_contains[0]
223
224 query = UserGroup.query()\
225 .order_by(func.length(UserGroup.users_group_name))\
226 .order_by(UserGroup.users_group_name)
227
228 if name_contains:
229 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
230 query = query.filter(
231 UserGroup.users_group_name.ilike(ilike_expression))
232 query = query.limit(limit)
233
234 acl_iter = query
235
236 return [
237 {
238 'id': obj.users_group_id,
239 'value': org_query,
240 'value_display': obj.users_group_name,
241 'type': 'user_group',
242 'url': h.route_path(
243 'user_group_profile', user_group_name=obj.users_group_name)
244 }
245 for obj in acl_iter]
246
214 def _get_hash_commit_list(self, auth_user, query):
247 def _get_hash_commit_list(self, auth_user, query):
215 org_query = query
248 org_query = query
216 if not query or len(query) < 3:
249 if not query or len(query) < 3:
217 return []
250 return []
218
251
219 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
252 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
220
253
221 if len(commit_hashes) != 1:
254 if len(commit_hashes) != 1:
222 return []
255 return []
223 commit_hash = commit_hashes[0]
256 commit_hash = commit_hashes[0]
224
257
225 searcher = searcher_from_config(self.request.registry.settings)
258 searcher = searcher_from_config(self.request.registry.settings)
226 result = searcher.search(
259 result = searcher.search(
227 'commit_id:%s*' % commit_hash, 'commit', auth_user,
260 'commit_id:%s*' % commit_hash, 'commit', auth_user,
228 raise_on_exc=False)
261 raise_on_exc=False)
229
262
230 return [
263 return [
231 {
264 {
232 'id': entry['commit_id'],
265 'id': entry['commit_id'],
233 'value': org_query,
266 'value': org_query,
234 'value_display': 'repo `{}` commit: {}'.format(
267 'value_display': 'repo `{}` commit: {}'.format(
235 entry['repository'], entry['commit_id']),
268 entry['repository'], entry['commit_id']),
236 'type': 'commit',
269 'type': 'commit',
237 'repo': entry['repository'],
270 'repo': entry['repository'],
238 'url': h.route_path(
271 'url': h.route_path(
239 'repo_commit',
272 'repo_commit',
240 repo_name=entry['repository'], commit_id=entry['commit_id'])
273 repo_name=entry['repository'], commit_id=entry['commit_id'])
241 }
274 }
242 for entry in result['results']]
275 for entry in result['results']]
243
276
244 @LoginRequired()
277 @LoginRequired()
245 @view_config(
278 @view_config(
246 route_name='repo_list_data', request_method='GET',
279 route_name='repo_list_data', request_method='GET',
247 renderer='json_ext', xhr=True)
280 renderer='json_ext', xhr=True)
248 def repo_list_data(self):
281 def repo_list_data(self):
249 _ = self.request.translate
282 _ = self.request.translate
250 self.load_default_context()
283 self.load_default_context()
251
284
252 query = self.request.GET.get('query')
285 query = self.request.GET.get('query')
253 repo_type = self.request.GET.get('repo_type')
286 repo_type = self.request.GET.get('repo_type')
254 log.debug('generating repo list, query:%s, repo_type:%s',
287 log.debug('generating repo list, query:%s, repo_type:%s',
255 query, repo_type)
288 query, repo_type)
256
289
257 res = []
290 res = []
258 repos = self._get_repo_list(query, repo_type=repo_type)
291 repos = self._get_repo_list(query, repo_type=repo_type)
259 if repos:
292 if repos:
260 res.append({
293 res.append({
261 'text': _('Repositories'),
294 'text': _('Repositories'),
262 'children': repos
295 'children': repos
263 })
296 })
264
297
265 data = {
298 data = {
266 'more': False,
299 'more': False,
267 'results': res
300 'results': res
268 }
301 }
269 return data
302 return data
270
303
271 @LoginRequired()
304 @LoginRequired()
272 @view_config(
305 @view_config(
273 route_name='goto_switcher_data', request_method='GET',
306 route_name='goto_switcher_data', request_method='GET',
274 renderer='json_ext', xhr=True)
307 renderer='json_ext', xhr=True)
275 def goto_switcher_data(self):
308 def goto_switcher_data(self):
276 c = self.load_default_context()
309 c = self.load_default_context()
277
310
278 _ = self.request.translate
311 _ = self.request.translate
279
312
280 query = self.request.GET.get('query')
313 query = self.request.GET.get('query')
281 log.debug('generating main filter data, query %s', query)
314 log.debug('generating main filter data, query %s', query)
282
315
283 default_search_val = u'Full text search for: `{}`'.format(query)
316 default_search_val = u'Full text search for: `{}`'.format(query)
284 res = []
317 res = []
285 if not query:
318 if not query:
286 return {'suggestions': res}
319 return {'suggestions': res}
287
320
288 res.append({
321 res.append({
289 'id': -1,
322 'id': -1,
290 'value': query,
323 'value': query,
291 'value_display': default_search_val,
324 'value_display': default_search_val,
292 'type': 'search',
325 'type': 'search',
293 'url': h.route_path(
326 'url': h.route_path(
294 'search', _query={'q': query})
327 'search', _query={'q': query})
295 })
328 })
296
329
297 repo_groups = self._get_repo_group_list(query)
330 repo_groups = self._get_repo_group_list(query)
298 for serialized_repo_group in repo_groups:
331 for serialized_repo_group in repo_groups:
299 res.append(serialized_repo_group)
332 res.append(serialized_repo_group)
300
333
301 repos = self._get_repo_list(query)
334 repos = self._get_repo_list(query)
302 for serialized_repo in repos:
335 for serialized_repo in repos:
303 res.append(serialized_repo)
336 res.append(serialized_repo)
304
337
305 # TODO(marcink): permissions for that ?
338 # TODO(marcink): permissions for that ?
306 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
339 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
307 if allowed_user_search:
340 if allowed_user_search:
308 users = self._get_user_list(query)
341 users = self._get_user_list(query)
309 for serialized_user in users:
342 for serialized_user in users:
310 res.append(serialized_user)
343 res.append(serialized_user)
311
344
345 user_groups = self._get_user_groups_list(query)
346 for serialized_user_group in user_groups:
347 res.append(serialized_user_group)
348
312 commits = self._get_hash_commit_list(c.auth_user, query)
349 commits = self._get_hash_commit_list(c.auth_user, query)
313 if commits:
350 if commits:
314 unique_repos = collections.OrderedDict()
351 unique_repos = collections.OrderedDict()
315 for commit in commits:
352 for commit in commits:
316 repo_name = commit['repo']
353 repo_name = commit['repo']
317 unique_repos.setdefault(repo_name, []).append(commit)
354 unique_repos.setdefault(repo_name, []).append(commit)
318
355
319 for repo, commits in unique_repos.items():
356 for repo, commits in unique_repos.items():
320 for commit in commits:
357 for commit in commits:
321 res.append(commit)
358 res.append(commit)
322
359
323 return {'suggestions': res}
360 return {'suggestions': res}
324
361
325 def _get_groups_and_repos(self, repo_group_id=None):
362 def _get_groups_and_repos(self, repo_group_id=None):
326 # repo groups groups
363 # repo groups groups
327 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
364 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
328 _perms = ['group.read', 'group.write', 'group.admin']
365 _perms = ['group.read', 'group.write', 'group.admin']
329 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
366 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
330 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
367 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
331 repo_group_list=repo_group_list_acl, admin=False)
368 repo_group_list=repo_group_list_acl, admin=False)
332
369
333 # repositories
370 # repositories
334 repo_list = Repository.get_all_repos(group_id=repo_group_id)
371 repo_list = Repository.get_all_repos(group_id=repo_group_id)
335 _perms = ['repository.read', 'repository.write', 'repository.admin']
372 _perms = ['repository.read', 'repository.write', 'repository.admin']
336 repo_list_acl = RepoList(repo_list, perm_set=_perms)
373 repo_list_acl = RepoList(repo_list, perm_set=_perms)
337 repo_data = RepoModel().get_repos_as_dict(
374 repo_data = RepoModel().get_repos_as_dict(
338 repo_list=repo_list_acl, admin=False)
375 repo_list=repo_list_acl, admin=False)
339
376
340 return repo_data, repo_group_data
377 return repo_data, repo_group_data
341
378
342 @LoginRequired()
379 @LoginRequired()
343 @view_config(
380 @view_config(
344 route_name='home', request_method='GET',
381 route_name='home', request_method='GET',
345 renderer='rhodecode:templates/index.mako')
382 renderer='rhodecode:templates/index.mako')
346 def main_page(self):
383 def main_page(self):
347 c = self.load_default_context()
384 c = self.load_default_context()
348 c.repo_group = None
385 c.repo_group = None
349
386
350 repo_data, repo_group_data = self._get_groups_and_repos()
387 repo_data, repo_group_data = self._get_groups_and_repos()
351 # json used to render the grids
388 # json used to render the grids
352 c.repos_data = json.dumps(repo_data)
389 c.repos_data = json.dumps(repo_data)
353 c.repo_groups_data = json.dumps(repo_group_data)
390 c.repo_groups_data = json.dumps(repo_group_data)
354
391
355 return self._get_template_context(c)
392 return self._get_template_context(c)
356
393
357 @LoginRequired()
394 @LoginRequired()
358 @HasRepoGroupPermissionAnyDecorator(
395 @HasRepoGroupPermissionAnyDecorator(
359 'group.read', 'group.write', 'group.admin')
396 'group.read', 'group.write', 'group.admin')
360 @view_config(
397 @view_config(
361 route_name='repo_group_home', request_method='GET',
398 route_name='repo_group_home', request_method='GET',
362 renderer='rhodecode:templates/index_repo_group.mako')
399 renderer='rhodecode:templates/index_repo_group.mako')
363 @view_config(
400 @view_config(
364 route_name='repo_group_home_slash', request_method='GET',
401 route_name='repo_group_home_slash', request_method='GET',
365 renderer='rhodecode:templates/index_repo_group.mako')
402 renderer='rhodecode:templates/index_repo_group.mako')
366 def repo_group_main_page(self):
403 def repo_group_main_page(self):
367 c = self.load_default_context()
404 c = self.load_default_context()
368 c.repo_group = self.request.db_repo_group
405 c.repo_group = self.request.db_repo_group
369 repo_data, repo_group_data = self._get_groups_and_repos(
406 repo_data, repo_group_data = self._get_groups_and_repos(
370 c.repo_group.group_id)
407 c.repo_group.group_id)
371
408
372 # json used to render the grids
409 # json used to render the grids
373 c.repos_data = json.dumps(repo_data)
410 c.repos_data = json.dumps(repo_data)
374 c.repo_groups_data = json.dumps(repo_group_data)
411 c.repo_groups_data = json.dumps(repo_group_data)
375
412
376 return self._get_template_context(c)
413 return self._get_template_context(c)
@@ -1,662 +1,667 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.mako"/>
2 <%inherit file="root.mako"/>
3
3
4 <%include file="/ejs_templates/templates.html"/>
4 <%include file="/ejs_templates/templates.html"/>
5
5
6 <div class="outerwrapper">
6 <div class="outerwrapper">
7 <!-- HEADER -->
7 <!-- HEADER -->
8 <div class="header">
8 <div class="header">
9 <div id="header-inner" class="wrapper">
9 <div id="header-inner" class="wrapper">
10 <div id="logo">
10 <div id="logo">
11 <div class="logo-wrapper">
11 <div class="logo-wrapper">
12 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
12 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
13 </div>
13 </div>
14 %if c.rhodecode_name:
14 %if c.rhodecode_name:
15 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
15 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
16 %endif
16 %endif
17 </div>
17 </div>
18 <!-- MENU BAR NAV -->
18 <!-- MENU BAR NAV -->
19 ${self.menu_bar_nav()}
19 ${self.menu_bar_nav()}
20 <!-- END MENU BAR NAV -->
20 <!-- END MENU BAR NAV -->
21 </div>
21 </div>
22 </div>
22 </div>
23 ${self.menu_bar_subnav()}
23 ${self.menu_bar_subnav()}
24 <!-- END HEADER -->
24 <!-- END HEADER -->
25
25
26 <!-- CONTENT -->
26 <!-- CONTENT -->
27 <div id="content" class="wrapper">
27 <div id="content" class="wrapper">
28
28
29 <rhodecode-toast id="notifications"></rhodecode-toast>
29 <rhodecode-toast id="notifications"></rhodecode-toast>
30
30
31 <div class="main">
31 <div class="main">
32 ${next.main()}
32 ${next.main()}
33 </div>
33 </div>
34 </div>
34 </div>
35 <!-- END CONTENT -->
35 <!-- END CONTENT -->
36
36
37 </div>
37 </div>
38 <!-- FOOTER -->
38 <!-- FOOTER -->
39 <div id="footer">
39 <div id="footer">
40 <div id="footer-inner" class="title wrapper">
40 <div id="footer-inner" class="title wrapper">
41 <div>
41 <div>
42 <p class="footer-link-right">
42 <p class="footer-link-right">
43 % if c.visual.show_version:
43 % if c.visual.show_version:
44 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
44 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
45 % endif
45 % endif
46 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
46 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
47 % if c.visual.rhodecode_support_url:
47 % if c.visual.rhodecode_support_url:
48 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
48 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
49 % endif
49 % endif
50 </p>
50 </p>
51 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
51 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
52 <p class="server-instance" style="display:${sid}">
52 <p class="server-instance" style="display:${sid}">
53 ## display hidden instance ID if specially defined
53 ## display hidden instance ID if specially defined
54 % if c.rhodecode_instanceid:
54 % if c.rhodecode_instanceid:
55 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
55 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
56 % endif
56 % endif
57 </p>
57 </p>
58 </div>
58 </div>
59 </div>
59 </div>
60 </div>
60 </div>
61
61
62 <!-- END FOOTER -->
62 <!-- END FOOTER -->
63
63
64 ### MAKO DEFS ###
64 ### MAKO DEFS ###
65
65
66 <%def name="menu_bar_subnav()">
66 <%def name="menu_bar_subnav()">
67 </%def>
67 </%def>
68
68
69 <%def name="breadcrumbs(class_='breadcrumbs')">
69 <%def name="breadcrumbs(class_='breadcrumbs')">
70 <div class="${class_}">
70 <div class="${class_}">
71 ${self.breadcrumbs_links()}
71 ${self.breadcrumbs_links()}
72 </div>
72 </div>
73 </%def>
73 </%def>
74
74
75 <%def name="admin_menu()">
75 <%def name="admin_menu()">
76 <ul class="admin_menu submenu">
76 <ul class="admin_menu submenu">
77 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
77 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
78 <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
78 <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
79 <li><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
79 <li><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
80 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
80 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
81 <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
81 <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
82 <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
82 <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
83 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
83 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
84 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
84 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
85 <li><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
85 <li><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
86 <li class="last"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
86 <li class="last"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
87 </ul>
87 </ul>
88 </%def>
88 </%def>
89
89
90
90
91 <%def name="dt_info_panel(elements)">
91 <%def name="dt_info_panel(elements)">
92 <dl class="dl-horizontal">
92 <dl class="dl-horizontal">
93 %for dt, dd, title, show_items in elements:
93 %for dt, dd, title, show_items in elements:
94 <dt>${dt}:</dt>
94 <dt>${dt}:</dt>
95 <dd title="${h.tooltip(title)}">
95 <dd title="${h.tooltip(title)}">
96 %if callable(dd):
96 %if callable(dd):
97 ## allow lazy evaluation of elements
97 ## allow lazy evaluation of elements
98 ${dd()}
98 ${dd()}
99 %else:
99 %else:
100 ${dd}
100 ${dd}
101 %endif
101 %endif
102 %if show_items:
102 %if show_items:
103 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
103 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
104 %endif
104 %endif
105 </dd>
105 </dd>
106
106
107 %if show_items:
107 %if show_items:
108 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
108 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
109 %for item in show_items:
109 %for item in show_items:
110 <dt></dt>
110 <dt></dt>
111 <dd>${item}</dd>
111 <dd>${item}</dd>
112 %endfor
112 %endfor
113 </div>
113 </div>
114 %endif
114 %endif
115
115
116 %endfor
116 %endfor
117 </dl>
117 </dl>
118 </%def>
118 </%def>
119
119
120
120
121 <%def name="gravatar(email, size=16)">
121 <%def name="gravatar(email, size=16)">
122 <%
122 <%
123 if (size > 16):
123 if (size > 16):
124 gravatar_class = 'gravatar gravatar-large'
124 gravatar_class = 'gravatar gravatar-large'
125 else:
125 else:
126 gravatar_class = 'gravatar'
126 gravatar_class = 'gravatar'
127 %>
127 %>
128 <%doc>
128 <%doc>
129 TODO: johbo: For now we serve double size images to make it smooth
129 TODO: johbo: For now we serve double size images to make it smooth
130 for retina. This is how it worked until now. Should be replaced
130 for retina. This is how it worked until now. Should be replaced
131 with a better solution at some point.
131 with a better solution at some point.
132 </%doc>
132 </%doc>
133 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
133 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
134 </%def>
134 </%def>
135
135
136
136
137 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
137 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
138 <% email = h.email_or_none(contact) %>
138 <% email = h.email_or_none(contact) %>
139 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
139 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
140 ${self.gravatar(email, size)}
140 ${self.gravatar(email, size)}
141 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
141 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
142 </div>
142 </div>
143 </%def>
143 </%def>
144
144
145
145
146 ## admin menu used for people that have some admin resources
146 ## admin menu used for people that have some admin resources
147 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
147 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
148 <ul class="submenu">
148 <ul class="submenu">
149 %if repositories:
149 %if repositories:
150 <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
150 <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
151 %endif
151 %endif
152 %if repository_groups:
152 %if repository_groups:
153 <li class="local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
153 <li class="local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
154 %endif
154 %endif
155 %if user_groups:
155 %if user_groups:
156 <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
156 <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
157 %endif
157 %endif
158 </ul>
158 </ul>
159 </%def>
159 </%def>
160
160
161 <%def name="repo_page_title(repo_instance)">
161 <%def name="repo_page_title(repo_instance)">
162 <div class="title-content">
162 <div class="title-content">
163 <div class="title-main">
163 <div class="title-main">
164 ## SVN/HG/GIT icons
164 ## SVN/HG/GIT icons
165 %if h.is_hg(repo_instance):
165 %if h.is_hg(repo_instance):
166 <i class="icon-hg"></i>
166 <i class="icon-hg"></i>
167 %endif
167 %endif
168 %if h.is_git(repo_instance):
168 %if h.is_git(repo_instance):
169 <i class="icon-git"></i>
169 <i class="icon-git"></i>
170 %endif
170 %endif
171 %if h.is_svn(repo_instance):
171 %if h.is_svn(repo_instance):
172 <i class="icon-svn"></i>
172 <i class="icon-svn"></i>
173 %endif
173 %endif
174
174
175 ## public/private
175 ## public/private
176 %if repo_instance.private:
176 %if repo_instance.private:
177 <i class="icon-repo-private"></i>
177 <i class="icon-repo-private"></i>
178 %else:
178 %else:
179 <i class="icon-repo-public"></i>
179 <i class="icon-repo-public"></i>
180 %endif
180 %endif
181
181
182 ## repo name with group name
182 ## repo name with group name
183 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
183 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
184
184
185 </div>
185 </div>
186
186
187 ## FORKED
187 ## FORKED
188 %if repo_instance.fork:
188 %if repo_instance.fork:
189 <p>
189 <p>
190 <i class="icon-code-fork"></i> ${_('Fork of')}
190 <i class="icon-code-fork"></i> ${_('Fork of')}
191 <a href="${h.route_path('repo_summary',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
191 <a href="${h.route_path('repo_summary',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
192 </p>
192 </p>
193 %endif
193 %endif
194
194
195 ## IMPORTED FROM REMOTE
195 ## IMPORTED FROM REMOTE
196 %if repo_instance.clone_uri:
196 %if repo_instance.clone_uri:
197 <p>
197 <p>
198 <i class="icon-code-fork"></i> ${_('Clone from')}
198 <i class="icon-code-fork"></i> ${_('Clone from')}
199 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
199 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
200 </p>
200 </p>
201 %endif
201 %endif
202
202
203 ## LOCKING STATUS
203 ## LOCKING STATUS
204 %if repo_instance.locked[0]:
204 %if repo_instance.locked[0]:
205 <p class="locking_locked">
205 <p class="locking_locked">
206 <i class="icon-repo-lock"></i>
206 <i class="icon-repo-lock"></i>
207 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
207 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
208 </p>
208 </p>
209 %elif repo_instance.enable_locking:
209 %elif repo_instance.enable_locking:
210 <p class="locking_unlocked">
210 <p class="locking_unlocked">
211 <i class="icon-repo-unlock"></i>
211 <i class="icon-repo-unlock"></i>
212 ${_('Repository not locked. Pull repository to lock it.')}
212 ${_('Repository not locked. Pull repository to lock it.')}
213 </p>
213 </p>
214 %endif
214 %endif
215
215
216 </div>
216 </div>
217 </%def>
217 </%def>
218
218
219 <%def name="repo_menu(active=None)">
219 <%def name="repo_menu(active=None)">
220 <%
220 <%
221 def is_active(selected):
221 def is_active(selected):
222 if selected == active:
222 if selected == active:
223 return "active"
223 return "active"
224 %>
224 %>
225
225
226 <!--- CONTEXT BAR -->
226 <!--- CONTEXT BAR -->
227 <div id="context-bar">
227 <div id="context-bar">
228 <div class="wrapper">
228 <div class="wrapper">
229 <ul id="context-pages" class="navigation horizontal-list">
229 <ul id="context-pages" class="navigation horizontal-list">
230 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
230 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
231 <li class="${is_active('changelog')}"><a class="menulink" href="${h.route_path('repo_changelog', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
231 <li class="${is_active('changelog')}"><a class="menulink" href="${h.route_path('repo_changelog', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
232 <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li>
232 <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li>
233 <li class="${is_active('compare')}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
233 <li class="${is_active('compare')}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 <li class="${is_active('showpullrequest')}">
236 <li class="${is_active('showpullrequest')}">
237 <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)}">
237 <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)}">
238 %if c.repository_pull_requests:
238 %if c.repository_pull_requests:
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 %endif
240 %endif
241 <div class="menulabel">${_('Pull Requests')}</div>
241 <div class="menulabel">${_('Pull Requests')}</div>
242 </a>
242 </a>
243 </li>
243 </li>
244 %endif
244 %endif
245 <li class="${is_active('options')}">
245 <li class="${is_active('options')}">
246 <a class="menulink dropdown">
246 <a class="menulink dropdown">
247 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
247 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
248 </a>
248 </a>
249 <ul class="submenu">
249 <ul class="submenu">
250 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
250 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
251 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
251 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
252 %endif
252 %endif
253 %if c.rhodecode_db_repo.fork:
253 %if c.rhodecode_db_repo.fork:
254 <li>
254 <li>
255 <a title="${h.tooltip(_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name))}"
255 <a title="${h.tooltip(_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name))}"
256 href="${h.route_path('repo_compare',
256 href="${h.route_path('repo_compare',
257 repo_name=c.rhodecode_db_repo.fork.repo_name,
257 repo_name=c.rhodecode_db_repo.fork.repo_name,
258 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
258 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
259 source_ref=c.rhodecode_db_repo.landing_rev[1],
259 source_ref=c.rhodecode_db_repo.landing_rev[1],
260 target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
260 target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
261 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
261 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
262 _query=dict(merge=1))}"
262 _query=dict(merge=1))}"
263 >
263 >
264 ${_('Compare fork')}
264 ${_('Compare fork')}
265 </a>
265 </a>
266 </li>
266 </li>
267 %endif
267 %endif
268
268
269 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
269 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
270
270
271 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
271 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
272 %if c.rhodecode_db_repo.locked[0]:
272 %if c.rhodecode_db_repo.locked[0]:
273 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
273 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
274 %else:
274 %else:
275 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
275 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
276 %endif
276 %endif
277 %endif
277 %endif
278 %if c.rhodecode_user.username != h.DEFAULT_USER:
278 %if c.rhodecode_user.username != h.DEFAULT_USER:
279 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
279 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
280 <li><a href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">${_('Fork')}</a></li>
280 <li><a href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">${_('Fork')}</a></li>
281 <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
281 <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
282 %endif
282 %endif
283 %endif
283 %endif
284 </ul>
284 </ul>
285 </li>
285 </li>
286 </ul>
286 </ul>
287 </div>
287 </div>
288 <div class="clear"></div>
288 <div class="clear"></div>
289 </div>
289 </div>
290 <!--- END CONTEXT BAR -->
290 <!--- END CONTEXT BAR -->
291
291
292 </%def>
292 </%def>
293
293
294 <%def name="usermenu(active=False)">
294 <%def name="usermenu(active=False)">
295 ## USER MENU
295 ## USER MENU
296 <li id="quick_login_li" class="${'active' if active else ''}">
296 <li id="quick_login_li" class="${'active' if active else ''}">
297 <a id="quick_login_link" class="menulink childs">
297 <a id="quick_login_link" class="menulink childs">
298 ${gravatar(c.rhodecode_user.email, 20)}
298 ${gravatar(c.rhodecode_user.email, 20)}
299 <span class="user">
299 <span class="user">
300 %if c.rhodecode_user.username != h.DEFAULT_USER:
300 %if c.rhodecode_user.username != h.DEFAULT_USER:
301 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
301 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
302 %else:
302 %else:
303 <span>${_('Sign in')}</span>
303 <span>${_('Sign in')}</span>
304 %endif
304 %endif
305 </span>
305 </span>
306 </a>
306 </a>
307
307
308 <div class="user-menu submenu">
308 <div class="user-menu submenu">
309 <div id="quick_login">
309 <div id="quick_login">
310 %if c.rhodecode_user.username == h.DEFAULT_USER:
310 %if c.rhodecode_user.username == h.DEFAULT_USER:
311 <h4>${_('Sign in to your account')}</h4>
311 <h4>${_('Sign in to your account')}</h4>
312 ${h.form(h.route_path('login', _query={'came_from': h.current_route_path(request)}), needs_csrf_token=False)}
312 ${h.form(h.route_path('login', _query={'came_from': h.current_route_path(request)}), needs_csrf_token=False)}
313 <div class="form form-vertical">
313 <div class="form form-vertical">
314 <div class="fields">
314 <div class="fields">
315 <div class="field">
315 <div class="field">
316 <div class="label">
316 <div class="label">
317 <label for="username">${_('Username')}:</label>
317 <label for="username">${_('Username')}:</label>
318 </div>
318 </div>
319 <div class="input">
319 <div class="input">
320 ${h.text('username',class_='focus',tabindex=1)}
320 ${h.text('username',class_='focus',tabindex=1)}
321 </div>
321 </div>
322
322
323 </div>
323 </div>
324 <div class="field">
324 <div class="field">
325 <div class="label">
325 <div class="label">
326 <label for="password">${_('Password')}:</label>
326 <label for="password">${_('Password')}:</label>
327 %if h.HasPermissionAny('hg.password_reset.enabled')():
327 %if h.HasPermissionAny('hg.password_reset.enabled')():
328 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
328 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
329 %endif
329 %endif
330 </div>
330 </div>
331 <div class="input">
331 <div class="input">
332 ${h.password('password',class_='focus',tabindex=2)}
332 ${h.password('password',class_='focus',tabindex=2)}
333 </div>
333 </div>
334 </div>
334 </div>
335 <div class="buttons">
335 <div class="buttons">
336 <div class="register">
336 <div class="register">
337 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
337 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
338 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
338 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
339 %endif
339 %endif
340 ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))}
340 ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))}
341 </div>
341 </div>
342 <div class="submit">
342 <div class="submit">
343 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
343 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
344 </div>
344 </div>
345 </div>
345 </div>
346 </div>
346 </div>
347 </div>
347 </div>
348 ${h.end_form()}
348 ${h.end_form()}
349 %else:
349 %else:
350 <div class="">
350 <div class="">
351 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
351 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
352 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
352 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
353 <div class="email">${c.rhodecode_user.email}</div>
353 <div class="email">${c.rhodecode_user.email}</div>
354 </div>
354 </div>
355 <div class="">
355 <div class="">
356 <ol class="links">
356 <ol class="links">
357 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
357 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
358 % if c.rhodecode_user.personal_repo_group:
358 % if c.rhodecode_user.personal_repo_group:
359 <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>
359 <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>
360 % endif
360 % endif
361 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
361 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
362
362
363 <li class="logout">
363 <li class="logout">
364 ${h.secure_form(h.route_path('logout'), request=request)}
364 ${h.secure_form(h.route_path('logout'), request=request)}
365 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
365 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
366 ${h.end_form()}
366 ${h.end_form()}
367 </li>
367 </li>
368 </ol>
368 </ol>
369 </div>
369 </div>
370 %endif
370 %endif
371 </div>
371 </div>
372 </div>
372 </div>
373 %if c.rhodecode_user.username != h.DEFAULT_USER:
373 %if c.rhodecode_user.username != h.DEFAULT_USER:
374 <div class="pill_container">
374 <div class="pill_container">
375 <a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a>
375 <a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a>
376 </div>
376 </div>
377 % endif
377 % endif
378 </li>
378 </li>
379 </%def>
379 </%def>
380
380
381 <%def name="menu_items(active=None)">
381 <%def name="menu_items(active=None)">
382 <%
382 <%
383 def is_active(selected):
383 def is_active(selected):
384 if selected == active:
384 if selected == active:
385 return "active"
385 return "active"
386 return ""
386 return ""
387 %>
387 %>
388 <ul id="quick" class="main_nav navigation horizontal-list">
388 <ul id="quick" class="main_nav navigation horizontal-list">
389
389
390 ## Main filter
390 ## Main filter
391 <li>
391 <li>
392 <div class="menulabel main_filter_box">
392 <div class="menulabel main_filter_box">
393 <div class="main_filter_input_box">
393 <div class="main_filter_input_box">
394 <input class="main_filter_input" id="main_filter" size="15" type="text" name="main_filter" placeholder="${_('search / go to...')}" value=""/>
394 <input class="main_filter_input" id="main_filter" size="15" type="text" name="main_filter" placeholder="${_('search / go to...')}" value=""/>
395 </div>
395 </div>
396 <div class="main_filter_help_box">
396 <div class="main_filter_help_box">
397 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
397 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
398 </div>
398 </div>
399 </div>
399 </div>
400
400
401 <div id="main_filter_help" style="display: none">
401 <div id="main_filter_help" style="display: none">
402 Use '/' key to quickly access this field.
402 Use '/' key to quickly access this field.
403 Enter name of repository, or repository group for quick search.
403 Enter name of repository, or repository group for quick search.
404
404
405 Prefix query to allow special search:
405 Prefix query to allow special search:
406
406
407 For usernames, e.g user:admin
407 user:admin, to search for usernames
408
408
409 For commit hash/id, e.g commit:efced4
409 user_group:devops, to search for user groups
410
411 commit:efced4, to search for commits
410
412
411 </div>
413 </div>
412 </li>
414 </li>
413
415
414 ## ROOT MENU
416 ## ROOT MENU
415 %if c.rhodecode_user.username != h.DEFAULT_USER:
417 %if c.rhodecode_user.username != h.DEFAULT_USER:
416 <li class="${is_active('journal')}">
418 <li class="${is_active('journal')}">
417 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
419 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
418 <div class="menulabel">${_('Journal')}</div>
420 <div class="menulabel">${_('Journal')}</div>
419 </a>
421 </a>
420 </li>
422 </li>
421 %else:
423 %else:
422 <li class="${is_active('journal')}">
424 <li class="${is_active('journal')}">
423 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
425 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
424 <div class="menulabel">${_('Public journal')}</div>
426 <div class="menulabel">${_('Public journal')}</div>
425 </a>
427 </a>
426 </li>
428 </li>
427 %endif
429 %endif
428 <li class="${is_active('gists')}">
430 <li class="${is_active('gists')}">
429 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
431 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
430 <div class="menulabel">${_('Gists')}</div>
432 <div class="menulabel">${_('Gists')}</div>
431 </a>
433 </a>
432 </li>
434 </li>
433 <li class="${is_active('search')}">
435 <li class="${is_active('search')}">
434 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
436 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
435 <div class="menulabel">${_('Search')}</div>
437 <div class="menulabel">${_('Search')}</div>
436 </a>
438 </a>
437 </li>
439 </li>
438 % if h.HasPermissionAll('hg.admin')('access admin main page'):
440 % if h.HasPermissionAll('hg.admin')('access admin main page'):
439 <li class="${is_active('admin')}">
441 <li class="${is_active('admin')}">
440 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
442 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
441 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
443 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
442 </a>
444 </a>
443 ${admin_menu()}
445 ${admin_menu()}
444 </li>
446 </li>
445 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
447 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
446 <li class="${is_active('admin')}">
448 <li class="${is_active('admin')}">
447 <a class="menulink childs" title="${_('Delegated Admin settings')}">
449 <a class="menulink childs" title="${_('Delegated Admin settings')}">
448 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
450 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
449 </a>
451 </a>
450 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
452 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
451 c.rhodecode_user.repository_groups_admin,
453 c.rhodecode_user.repository_groups_admin,
452 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
454 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
453 </li>
455 </li>
454 % endif
456 % endif
455 ## render extra user menu
457 ## render extra user menu
456 ${usermenu(active=(active=='my_account'))}
458 ${usermenu(active=(active=='my_account'))}
457
459
458 % if c.debug_style:
460 % if c.debug_style:
459 <li class="${is_active('debug_style')}">
461 <li class="${is_active('debug_style')}">
460 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
462 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
461 <div class="menulabel">${_('Style')}</div>
463 <div class="menulabel">${_('Style')}</div>
462 </a>
464 </a>
463 </li>
465 </li>
464 % endif
466 % endif
465 </ul>
467 </ul>
466
468
467 <script type="text/javascript">
469 <script type="text/javascript">
468 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
470 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
469
471
470 var formatRepoResult = function(result, container, query, escapeMarkup) {
472 var formatRepoResult = function(result, container, query, escapeMarkup) {
471 return function(data, escapeMarkup) {
473 return function(data, escapeMarkup) {
472 if (!data.repo_id){
474 if (!data.repo_id){
473 return data.text; // optgroup text Repositories
475 return data.text; // optgroup text Repositories
474 }
476 }
475
477
476 var tmpl = '';
478 var tmpl = '';
477 var repoType = data['repo_type'];
479 var repoType = data['repo_type'];
478 var repoName = data['text'];
480 var repoName = data['text'];
479
481
480 if(data && data.type == 'repo'){
482 if(data && data.type == 'repo'){
481 if(repoType === 'hg'){
483 if(repoType === 'hg'){
482 tmpl += '<i class="icon-hg"></i> ';
484 tmpl += '<i class="icon-hg"></i> ';
483 }
485 }
484 else if(repoType === 'git'){
486 else if(repoType === 'git'){
485 tmpl += '<i class="icon-git"></i> ';
487 tmpl += '<i class="icon-git"></i> ';
486 }
488 }
487 else if(repoType === 'svn'){
489 else if(repoType === 'svn'){
488 tmpl += '<i class="icon-svn"></i> ';
490 tmpl += '<i class="icon-svn"></i> ';
489 }
491 }
490 if(data['private']){
492 if(data['private']){
491 tmpl += '<i class="icon-lock" ></i> ';
493 tmpl += '<i class="icon-lock" ></i> ';
492 }
494 }
493 else if(visualShowPublicIcon){
495 else if(visualShowPublicIcon){
494 tmpl += '<i class="icon-unlock-alt"></i> ';
496 tmpl += '<i class="icon-unlock-alt"></i> ';
495 }
497 }
496 }
498 }
497 tmpl += escapeMarkup(repoName);
499 tmpl += escapeMarkup(repoName);
498 return tmpl;
500 return tmpl;
499
501
500 }(result, escapeMarkup);
502 }(result, escapeMarkup);
501 };
503 };
502
504
503
505
504 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
506 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
505
507
506 if (value.split(':').length === 2) {
508 if (value.split(':').length === 2) {
507 value = value.split(':')[1]
509 value = value.split(':')[1]
508 }
510 }
509
511
510 var searchType = data['type'];
512 var searchType = data['type'];
511 var valueDisplay = data['value_display'];
513 var valueDisplay = data['value_display'];
512
514
513 var escapeRegExChars = function (value) {
515 var escapeRegExChars = function (value) {
514 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
516 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
515 };
517 };
516 var pattern = '(' + escapeRegExChars(value) + ')';
518 var pattern = '(' + escapeRegExChars(value) + ')';
517
519
518 // highlight match
520 // highlight match
519 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
521 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
520 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
522 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
521
523
522 var icon = '';
524 var icon = '';
523
525
524 if (searchType === 'search') {
526 if (searchType === 'search') {
525 icon += '<i class="icon-more"></i> ';
527 icon += '<i class="icon-more"></i> ';
526 }
528 }
527 else if (searchType === 'repo') {
529 else if (searchType === 'repo') {
528 if (data['repo_type'] === 'hg') {
530 if (data['repo_type'] === 'hg') {
529 icon += '<i class="icon-hg"></i> ';
531 icon += '<i class="icon-hg"></i> ';
530 }
532 }
531 else if (data['repo_type'] === 'git') {
533 else if (data['repo_type'] === 'git') {
532 icon += '<i class="icon-git"></i> ';
534 icon += '<i class="icon-git"></i> ';
533 }
535 }
534 else if (data['repo_type'] === 'svn') {
536 else if (data['repo_type'] === 'svn') {
535 icon += '<i class="icon-svn"></i> ';
537 icon += '<i class="icon-svn"></i> ';
536 }
538 }
537 if (data['private']) {
539 if (data['private']) {
538 icon += '<i class="icon-lock" ></i> ';
540 icon += '<i class="icon-lock" ></i> ';
539 }
541 }
540 else if (visualShowPublicIcon) {
542 else if (visualShowPublicIcon) {
541 icon += '<i class="icon-unlock-alt"></i> ';
543 icon += '<i class="icon-unlock-alt"></i> ';
542 }
544 }
543 }
545 }
544 else if (searchType === 'repo_group') {
546 else if (searchType === 'repo_group') {
545 icon += '<i class="icon-folder-close"></i> ';
547 icon += '<i class="icon-folder-close"></i> ';
546 }
548 }
549 else if (searchType === 'user_group') {
550 icon += '<i class="icon-group"></i> ';
551 }
547 else if (searchType === 'user') {
552 else if (searchType === 'user') {
548 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
553 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
549 }
554 }
550 else if (searchType === 'commit') {
555 else if (searchType === 'commit') {
551 icon += '<i class="icon-tag"></i>';
556 icon += '<i class="icon-tag"></i>';
552 }
557 }
553
558
554 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
559 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
555 return tmpl.format(icon, valueDisplay);
560 return tmpl.format(icon, valueDisplay);
556 };
561 };
557
562
558 var handleSelect = function(element, suggestion) {
563 var handleSelect = function(element, suggestion) {
559 window.location = suggestion['url'];
564 window.location = suggestion['url'];
560 };
565 };
561 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
566 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
562 if (queryLowerCase.split(':').length === 2) {
567 if (queryLowerCase.split(':').length === 2) {
563 queryLowerCase = queryLowerCase.split(':')[1]
568 queryLowerCase = queryLowerCase.split(':')[1]
564 }
569 }
565 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
570 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
566 };
571 };
567
572
568 $('#main_filter').autocomplete({
573 $('#main_filter').autocomplete({
569 serviceUrl: pyroutes.url('goto_switcher_data'),
574 serviceUrl: pyroutes.url('goto_switcher_data'),
570 minChars:2,
575 minChars:2,
571 maxHeight:400,
576 maxHeight:400,
572 deferRequestBy: 300, //miliseconds
577 deferRequestBy: 300, //miliseconds
573 tabDisabled: true,
578 tabDisabled: true,
574 autoSelectFirst: true,
579 autoSelectFirst: true,
575 formatResult: autocompleteMainFilterFormatResult,
580 formatResult: autocompleteMainFilterFormatResult,
576 lookupFilter: autocompleteMainFilterResult,
581 lookupFilter: autocompleteMainFilterResult,
577 onSelect: function(element, suggestion){
582 onSelect: function(element, suggestion){
578 handleSelect(element, suggestion);
583 handleSelect(element, suggestion);
579 return false;
584 return false;
580 }
585 }
581 });
586 });
582
587
583 showMainFilterBox = function () {
588 showMainFilterBox = function () {
584 $('#main_filter_help').toggle();
589 $('#main_filter_help').toggle();
585 }
590 }
586
591
587 </script>
592 </script>
588 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
593 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
589 </%def>
594 </%def>
590
595
591 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
596 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
592 <div class="modal-dialog">
597 <div class="modal-dialog">
593 <div class="modal-content">
598 <div class="modal-content">
594 <div class="modal-header">
599 <div class="modal-header">
595 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
600 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
596 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
601 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
597 </div>
602 </div>
598 <div class="modal-body">
603 <div class="modal-body">
599 <div class="block-left">
604 <div class="block-left">
600 <table class="keyboard-mappings">
605 <table class="keyboard-mappings">
601 <tbody>
606 <tbody>
602 <tr>
607 <tr>
603 <th></th>
608 <th></th>
604 <th>${_('Site-wide shortcuts')}</th>
609 <th>${_('Site-wide shortcuts')}</th>
605 </tr>
610 </tr>
606 <%
611 <%
607 elems = [
612 elems = [
608 ('/', 'Use quick search box'),
613 ('/', 'Use quick search box'),
609 ('g h', 'Goto home page'),
614 ('g h', 'Goto home page'),
610 ('g g', 'Goto my private gists page'),
615 ('g g', 'Goto my private gists page'),
611 ('g G', 'Goto my public gists page'),
616 ('g G', 'Goto my public gists page'),
612 ('n r', 'New repository page'),
617 ('n r', 'New repository page'),
613 ('n g', 'New gist page'),
618 ('n g', 'New gist page'),
614 ]
619 ]
615 %>
620 %>
616 %for key, desc in elems:
621 %for key, desc in elems:
617 <tr>
622 <tr>
618 <td class="keys">
623 <td class="keys">
619 <span class="key tag">${key}</span>
624 <span class="key tag">${key}</span>
620 </td>
625 </td>
621 <td>${desc}</td>
626 <td>${desc}</td>
622 </tr>
627 </tr>
623 %endfor
628 %endfor
624 </tbody>
629 </tbody>
625 </table>
630 </table>
626 </div>
631 </div>
627 <div class="block-left">
632 <div class="block-left">
628 <table class="keyboard-mappings">
633 <table class="keyboard-mappings">
629 <tbody>
634 <tbody>
630 <tr>
635 <tr>
631 <th></th>
636 <th></th>
632 <th>${_('Repositories')}</th>
637 <th>${_('Repositories')}</th>
633 </tr>
638 </tr>
634 <%
639 <%
635 elems = [
640 elems = [
636 ('g s', 'Goto summary page'),
641 ('g s', 'Goto summary page'),
637 ('g c', 'Goto changelog page'),
642 ('g c', 'Goto changelog page'),
638 ('g f', 'Goto files page'),
643 ('g f', 'Goto files page'),
639 ('g F', 'Goto files page with file search activated'),
644 ('g F', 'Goto files page with file search activated'),
640 ('g p', 'Goto pull requests page'),
645 ('g p', 'Goto pull requests page'),
641 ('g o', 'Goto repository settings'),
646 ('g o', 'Goto repository settings'),
642 ('g O', 'Goto repository permissions settings'),
647 ('g O', 'Goto repository permissions settings'),
643 ]
648 ]
644 %>
649 %>
645 %for key, desc in elems:
650 %for key, desc in elems:
646 <tr>
651 <tr>
647 <td class="keys">
652 <td class="keys">
648 <span class="key tag">${key}</span>
653 <span class="key tag">${key}</span>
649 </td>
654 </td>
650 <td>${desc}</td>
655 <td>${desc}</td>
651 </tr>
656 </tr>
652 %endfor
657 %endfor
653 </tbody>
658 </tbody>
654 </table>
659 </table>
655 </div>
660 </div>
656 </div>
661 </div>
657 <div class="modal-footer">
662 <div class="modal-footer">
658 </div>
663 </div>
659 </div><!-- /.modal-content -->
664 </div><!-- /.modal-content -->
660 </div><!-- /.modal-dialog -->
665 </div><!-- /.modal-dialog -->
661 </div><!-- /.modal -->
666 </div><!-- /.modal -->
662
667
General Comments 0
You need to be logged in to leave comments. Login now