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