##// END OF EJS Templates
license: add helpers to allow users hide license warnings.
marcink -
r4453:a39b0f4b default
parent child Browse files
Show More
@@ -1,617 +1,618 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 The base Controller API
22 The base Controller API
23 Provides the BaseController class for subclassing. And usage in different
23 Provides the BaseController class for subclassing. And usage in different
24 controllers
24 controllers
25 """
25 """
26
26
27 import logging
27 import logging
28 import socket
28 import socket
29
29
30 import markupsafe
30 import markupsafe
31 import ipaddress
31 import ipaddress
32
32
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.apps._base import TemplateArgs
38 from rhodecode.apps._base import TemplateArgs
39 from rhodecode.authentication.base import VCS_TYPE
39 from rhodecode.authentication.base import VCS_TYPE
40 from rhodecode.lib import auth, utils2
40 from rhodecode.lib import auth, utils2
41 from rhodecode.lib import helpers as h
41 from rhodecode.lib import helpers as h
42 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
42 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
43 from rhodecode.lib.exceptions import UserCreationError
43 from rhodecode.lib.exceptions import UserCreationError
44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
45 from rhodecode.lib.utils2 import (
45 from rhodecode.lib.utils2 import (
46 str2bool, safe_unicode, AttributeDict, safe_int, sha1, aslist, safe_str)
46 str2bool, safe_unicode, AttributeDict, safe_int, sha1, aslist, safe_str)
47 from rhodecode.model.db import Repository, User, ChangesetComment, UserBookmark
47 from rhodecode.model.db import Repository, User, ChangesetComment, UserBookmark
48 from rhodecode.model.notification import NotificationModel
48 from rhodecode.model.notification import NotificationModel
49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 def _filter_proxy(ip):
54 def _filter_proxy(ip):
55 """
55 """
56 Passed in IP addresses in HEADERS can be in a special format of multiple
56 Passed in IP addresses in HEADERS can be in a special format of multiple
57 ips. Those comma separated IPs are passed from various proxies in the
57 ips. Those comma separated IPs are passed from various proxies in the
58 chain of request processing. The left-most being the original client.
58 chain of request processing. The left-most being the original client.
59 We only care about the first IP which came from the org. client.
59 We only care about the first IP which came from the org. client.
60
60
61 :param ip: ip string from headers
61 :param ip: ip string from headers
62 """
62 """
63 if ',' in ip:
63 if ',' in ip:
64 _ips = ip.split(',')
64 _ips = ip.split(',')
65 _first_ip = _ips[0].strip()
65 _first_ip = _ips[0].strip()
66 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
66 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
67 return _first_ip
67 return _first_ip
68 return ip
68 return ip
69
69
70
70
71 def _filter_port(ip):
71 def _filter_port(ip):
72 """
72 """
73 Removes a port from ip, there are 4 main cases to handle here.
73 Removes a port from ip, there are 4 main cases to handle here.
74 - ipv4 eg. 127.0.0.1
74 - ipv4 eg. 127.0.0.1
75 - ipv6 eg. ::1
75 - ipv6 eg. ::1
76 - ipv4+port eg. 127.0.0.1:8080
76 - ipv4+port eg. 127.0.0.1:8080
77 - ipv6+port eg. [::1]:8080
77 - ipv6+port eg. [::1]:8080
78
78
79 :param ip:
79 :param ip:
80 """
80 """
81 def is_ipv6(ip_addr):
81 def is_ipv6(ip_addr):
82 if hasattr(socket, 'inet_pton'):
82 if hasattr(socket, 'inet_pton'):
83 try:
83 try:
84 socket.inet_pton(socket.AF_INET6, ip_addr)
84 socket.inet_pton(socket.AF_INET6, ip_addr)
85 except socket.error:
85 except socket.error:
86 return False
86 return False
87 else:
87 else:
88 # fallback to ipaddress
88 # fallback to ipaddress
89 try:
89 try:
90 ipaddress.IPv6Address(safe_unicode(ip_addr))
90 ipaddress.IPv6Address(safe_unicode(ip_addr))
91 except Exception:
91 except Exception:
92 return False
92 return False
93 return True
93 return True
94
94
95 if ':' not in ip: # must be ipv4 pure ip
95 if ':' not in ip: # must be ipv4 pure ip
96 return ip
96 return ip
97
97
98 if '[' in ip and ']' in ip: # ipv6 with port
98 if '[' in ip and ']' in ip: # ipv6 with port
99 return ip.split(']')[0][1:].lower()
99 return ip.split(']')[0][1:].lower()
100
100
101 # must be ipv6 or ipv4 with port
101 # must be ipv6 or ipv4 with port
102 if is_ipv6(ip):
102 if is_ipv6(ip):
103 return ip
103 return ip
104 else:
104 else:
105 ip, _port = ip.split(':')[:2] # means ipv4+port
105 ip, _port = ip.split(':')[:2] # means ipv4+port
106 return ip
106 return ip
107
107
108
108
109 def get_ip_addr(environ):
109 def get_ip_addr(environ):
110 proxy_key = 'HTTP_X_REAL_IP'
110 proxy_key = 'HTTP_X_REAL_IP'
111 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
111 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
112 def_key = 'REMOTE_ADDR'
112 def_key = 'REMOTE_ADDR'
113 _filters = lambda x: _filter_port(_filter_proxy(x))
113 _filters = lambda x: _filter_port(_filter_proxy(x))
114
114
115 ip = environ.get(proxy_key)
115 ip = environ.get(proxy_key)
116 if ip:
116 if ip:
117 return _filters(ip)
117 return _filters(ip)
118
118
119 ip = environ.get(proxy_key2)
119 ip = environ.get(proxy_key2)
120 if ip:
120 if ip:
121 return _filters(ip)
121 return _filters(ip)
122
122
123 ip = environ.get(def_key, '0.0.0.0')
123 ip = environ.get(def_key, '0.0.0.0')
124 return _filters(ip)
124 return _filters(ip)
125
125
126
126
127 def get_server_ip_addr(environ, log_errors=True):
127 def get_server_ip_addr(environ, log_errors=True):
128 hostname = environ.get('SERVER_NAME')
128 hostname = environ.get('SERVER_NAME')
129 try:
129 try:
130 return socket.gethostbyname(hostname)
130 return socket.gethostbyname(hostname)
131 except Exception as e:
131 except Exception as e:
132 if log_errors:
132 if log_errors:
133 # in some cases this lookup is not possible, and we don't want to
133 # in some cases this lookup is not possible, and we don't want to
134 # make it an exception in logs
134 # make it an exception in logs
135 log.exception('Could not retrieve server ip address: %s', e)
135 log.exception('Could not retrieve server ip address: %s', e)
136 return hostname
136 return hostname
137
137
138
138
139 def get_server_port(environ):
139 def get_server_port(environ):
140 return environ.get('SERVER_PORT')
140 return environ.get('SERVER_PORT')
141
141
142
142
143 def get_access_path(environ):
143 def get_access_path(environ):
144 path = environ.get('PATH_INFO')
144 path = environ.get('PATH_INFO')
145 org_req = environ.get('pylons.original_request')
145 org_req = environ.get('pylons.original_request')
146 if org_req:
146 if org_req:
147 path = org_req.environ.get('PATH_INFO')
147 path = org_req.environ.get('PATH_INFO')
148 return path
148 return path
149
149
150
150
151 def get_user_agent(environ):
151 def get_user_agent(environ):
152 return environ.get('HTTP_USER_AGENT')
152 return environ.get('HTTP_USER_AGENT')
153
153
154
154
155 def vcs_operation_context(
155 def vcs_operation_context(
156 environ, repo_name, username, action, scm, check_locking=True,
156 environ, repo_name, username, action, scm, check_locking=True,
157 is_shadow_repo=False, check_branch_perms=False, detect_force_push=False):
157 is_shadow_repo=False, check_branch_perms=False, detect_force_push=False):
158 """
158 """
159 Generate the context for a vcs operation, e.g. push or pull.
159 Generate the context for a vcs operation, e.g. push or pull.
160
160
161 This context is passed over the layers so that hooks triggered by the
161 This context is passed over the layers so that hooks triggered by the
162 vcs operation know details like the user, the user's IP address etc.
162 vcs operation know details like the user, the user's IP address etc.
163
163
164 :param check_locking: Allows to switch of the computation of the locking
164 :param check_locking: Allows to switch of the computation of the locking
165 data. This serves mainly the need of the simplevcs middleware to be
165 data. This serves mainly the need of the simplevcs middleware to be
166 able to disable this for certain operations.
166 able to disable this for certain operations.
167
167
168 """
168 """
169 # Tri-state value: False: unlock, None: nothing, True: lock
169 # Tri-state value: False: unlock, None: nothing, True: lock
170 make_lock = None
170 make_lock = None
171 locked_by = [None, None, None]
171 locked_by = [None, None, None]
172 is_anonymous = username == User.DEFAULT_USER
172 is_anonymous = username == User.DEFAULT_USER
173 user = User.get_by_username(username)
173 user = User.get_by_username(username)
174 if not is_anonymous and check_locking:
174 if not is_anonymous and check_locking:
175 log.debug('Checking locking on repository "%s"', repo_name)
175 log.debug('Checking locking on repository "%s"', repo_name)
176 repo = Repository.get_by_repo_name(repo_name)
176 repo = Repository.get_by_repo_name(repo_name)
177 make_lock, __, locked_by = repo.get_locking_state(
177 make_lock, __, locked_by = repo.get_locking_state(
178 action, user.user_id)
178 action, user.user_id)
179 user_id = user.user_id
179 user_id = user.user_id
180 settings_model = VcsSettingsModel(repo=repo_name)
180 settings_model = VcsSettingsModel(repo=repo_name)
181 ui_settings = settings_model.get_ui_settings()
181 ui_settings = settings_model.get_ui_settings()
182
182
183 # NOTE(marcink): This should be also in sync with
183 # NOTE(marcink): This should be also in sync with
184 # rhodecode/apps/ssh_support/lib/backends/base.py:update_environment scm_data
184 # rhodecode/apps/ssh_support/lib/backends/base.py:update_environment scm_data
185 store = [x for x in ui_settings if x.key == '/']
185 store = [x for x in ui_settings if x.key == '/']
186 repo_store = ''
186 repo_store = ''
187 if store:
187 if store:
188 repo_store = store[0].value
188 repo_store = store[0].value
189
189
190 scm_data = {
190 scm_data = {
191 'ip': get_ip_addr(environ),
191 'ip': get_ip_addr(environ),
192 'username': username,
192 'username': username,
193 'user_id': user_id,
193 'user_id': user_id,
194 'action': action,
194 'action': action,
195 'repository': repo_name,
195 'repository': repo_name,
196 'scm': scm,
196 'scm': scm,
197 'config': rhodecode.CONFIG['__file__'],
197 'config': rhodecode.CONFIG['__file__'],
198 'repo_store': repo_store,
198 'repo_store': repo_store,
199 'make_lock': make_lock,
199 'make_lock': make_lock,
200 'locked_by': locked_by,
200 'locked_by': locked_by,
201 'server_url': utils2.get_server_url(environ),
201 'server_url': utils2.get_server_url(environ),
202 'user_agent': get_user_agent(environ),
202 'user_agent': get_user_agent(environ),
203 'hooks': get_enabled_hook_classes(ui_settings),
203 'hooks': get_enabled_hook_classes(ui_settings),
204 'is_shadow_repo': is_shadow_repo,
204 'is_shadow_repo': is_shadow_repo,
205 'detect_force_push': detect_force_push,
205 'detect_force_push': detect_force_push,
206 'check_branch_perms': check_branch_perms,
206 'check_branch_perms': check_branch_perms,
207 }
207 }
208 return scm_data
208 return scm_data
209
209
210
210
211 class BasicAuth(AuthBasicAuthenticator):
211 class BasicAuth(AuthBasicAuthenticator):
212
212
213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
214 initial_call_detection=False, acl_repo_name=None, rc_realm=''):
214 initial_call_detection=False, acl_repo_name=None, rc_realm=''):
215 self.realm = realm
215 self.realm = realm
216 self.rc_realm = rc_realm
216 self.rc_realm = rc_realm
217 self.initial_call = initial_call_detection
217 self.initial_call = initial_call_detection
218 self.authfunc = authfunc
218 self.authfunc = authfunc
219 self.registry = registry
219 self.registry = registry
220 self.acl_repo_name = acl_repo_name
220 self.acl_repo_name = acl_repo_name
221 self._rc_auth_http_code = auth_http_code
221 self._rc_auth_http_code = auth_http_code
222
222
223 def _get_response_from_code(self, http_code):
223 def _get_response_from_code(self, http_code):
224 try:
224 try:
225 return get_exception(safe_int(http_code))
225 return get_exception(safe_int(http_code))
226 except Exception:
226 except Exception:
227 log.exception('Failed to fetch response for code %s', http_code)
227 log.exception('Failed to fetch response for code %s', http_code)
228 return HTTPForbidden
228 return HTTPForbidden
229
229
230 def get_rc_realm(self):
230 def get_rc_realm(self):
231 return safe_str(self.rc_realm)
231 return safe_str(self.rc_realm)
232
232
233 def build_authentication(self):
233 def build_authentication(self):
234 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
234 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
235 if self._rc_auth_http_code and not self.initial_call:
235 if self._rc_auth_http_code and not self.initial_call:
236 # return alternative HTTP code if alternative http return code
236 # return alternative HTTP code if alternative http return code
237 # is specified in RhodeCode config, but ONLY if it's not the
237 # is specified in RhodeCode config, but ONLY if it's not the
238 # FIRST call
238 # FIRST call
239 custom_response_klass = self._get_response_from_code(
239 custom_response_klass = self._get_response_from_code(
240 self._rc_auth_http_code)
240 self._rc_auth_http_code)
241 return custom_response_klass(headers=head)
241 return custom_response_klass(headers=head)
242 return HTTPUnauthorized(headers=head)
242 return HTTPUnauthorized(headers=head)
243
243
244 def authenticate(self, environ):
244 def authenticate(self, environ):
245 authorization = AUTHORIZATION(environ)
245 authorization = AUTHORIZATION(environ)
246 if not authorization:
246 if not authorization:
247 return self.build_authentication()
247 return self.build_authentication()
248 (authmeth, auth) = authorization.split(' ', 1)
248 (authmeth, auth) = authorization.split(' ', 1)
249 if 'basic' != authmeth.lower():
249 if 'basic' != authmeth.lower():
250 return self.build_authentication()
250 return self.build_authentication()
251 auth = auth.strip().decode('base64')
251 auth = auth.strip().decode('base64')
252 _parts = auth.split(':', 1)
252 _parts = auth.split(':', 1)
253 if len(_parts) == 2:
253 if len(_parts) == 2:
254 username, password = _parts
254 username, password = _parts
255 auth_data = self.authfunc(
255 auth_data = self.authfunc(
256 username, password, environ, VCS_TYPE,
256 username, password, environ, VCS_TYPE,
257 registry=self.registry, acl_repo_name=self.acl_repo_name)
257 registry=self.registry, acl_repo_name=self.acl_repo_name)
258 if auth_data:
258 if auth_data:
259 return {'username': username, 'auth_data': auth_data}
259 return {'username': username, 'auth_data': auth_data}
260 if username and password:
260 if username and password:
261 # we mark that we actually executed authentication once, at
261 # we mark that we actually executed authentication once, at
262 # that point we can use the alternative auth code
262 # that point we can use the alternative auth code
263 self.initial_call = False
263 self.initial_call = False
264
264
265 return self.build_authentication()
265 return self.build_authentication()
266
266
267 __call__ = authenticate
267 __call__ = authenticate
268
268
269
269
270 def calculate_version_hash(config):
270 def calculate_version_hash(config):
271 return sha1(
271 return sha1(
272 config.get('beaker.session.secret', '') +
272 config.get('beaker.session.secret', '') +
273 rhodecode.__version__)[:8]
273 rhodecode.__version__)[:8]
274
274
275
275
276 def get_current_lang(request):
276 def get_current_lang(request):
277 # NOTE(marcink): remove after pyramid move
277 # NOTE(marcink): remove after pyramid move
278 try:
278 try:
279 return translation.get_lang()[0]
279 return translation.get_lang()[0]
280 except:
280 except:
281 pass
281 pass
282
282
283 return getattr(request, '_LOCALE_', request.locale_name)
283 return getattr(request, '_LOCALE_', request.locale_name)
284
284
285
285
286 def attach_context_attributes(context, request, user_id=None, is_api=None):
286 def attach_context_attributes(context, request, user_id=None, is_api=None):
287 """
287 """
288 Attach variables into template context called `c`.
288 Attach variables into template context called `c`.
289 """
289 """
290 config = request.registry.settings
290 config = request.registry.settings
291
291
292 rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
292 rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
293 context.rc_config = rc_config
293 context.rc_config = rc_config
294 context.rhodecode_version = rhodecode.__version__
294 context.rhodecode_version = rhodecode.__version__
295 context.rhodecode_edition = config.get('rhodecode.edition')
295 context.rhodecode_edition = config.get('rhodecode.edition')
296 # unique secret + version does not leak the version but keep consistency
296 # unique secret + version does not leak the version but keep consistency
297 context.rhodecode_version_hash = calculate_version_hash(config)
297 context.rhodecode_version_hash = calculate_version_hash(config)
298
298
299 # Default language set for the incoming request
299 # Default language set for the incoming request
300 context.language = get_current_lang(request)
300 context.language = get_current_lang(request)
301
301
302 # Visual options
302 # Visual options
303 context.visual = AttributeDict({})
303 context.visual = AttributeDict({})
304
304
305 # DB stored Visual Items
305 # DB stored Visual Items
306 context.visual.show_public_icon = str2bool(
306 context.visual.show_public_icon = str2bool(
307 rc_config.get('rhodecode_show_public_icon'))
307 rc_config.get('rhodecode_show_public_icon'))
308 context.visual.show_private_icon = str2bool(
308 context.visual.show_private_icon = str2bool(
309 rc_config.get('rhodecode_show_private_icon'))
309 rc_config.get('rhodecode_show_private_icon'))
310 context.visual.stylify_metatags = str2bool(
310 context.visual.stylify_metatags = str2bool(
311 rc_config.get('rhodecode_stylify_metatags'))
311 rc_config.get('rhodecode_stylify_metatags'))
312 context.visual.dashboard_items = safe_int(
312 context.visual.dashboard_items = safe_int(
313 rc_config.get('rhodecode_dashboard_items', 100))
313 rc_config.get('rhodecode_dashboard_items', 100))
314 context.visual.admin_grid_items = safe_int(
314 context.visual.admin_grid_items = safe_int(
315 rc_config.get('rhodecode_admin_grid_items', 100))
315 rc_config.get('rhodecode_admin_grid_items', 100))
316 context.visual.show_revision_number = str2bool(
316 context.visual.show_revision_number = str2bool(
317 rc_config.get('rhodecode_show_revision_number', True))
317 rc_config.get('rhodecode_show_revision_number', True))
318 context.visual.show_sha_length = safe_int(
318 context.visual.show_sha_length = safe_int(
319 rc_config.get('rhodecode_show_sha_length', 100))
319 rc_config.get('rhodecode_show_sha_length', 100))
320 context.visual.repository_fields = str2bool(
320 context.visual.repository_fields = str2bool(
321 rc_config.get('rhodecode_repository_fields'))
321 rc_config.get('rhodecode_repository_fields'))
322 context.visual.show_version = str2bool(
322 context.visual.show_version = str2bool(
323 rc_config.get('rhodecode_show_version'))
323 rc_config.get('rhodecode_show_version'))
324 context.visual.use_gravatar = str2bool(
324 context.visual.use_gravatar = str2bool(
325 rc_config.get('rhodecode_use_gravatar'))
325 rc_config.get('rhodecode_use_gravatar'))
326 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
326 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
327 context.visual.default_renderer = rc_config.get(
327 context.visual.default_renderer = rc_config.get(
328 'rhodecode_markup_renderer', 'rst')
328 'rhodecode_markup_renderer', 'rst')
329 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
329 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
330 context.visual.rhodecode_support_url = \
330 context.visual.rhodecode_support_url = \
331 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
331 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
332
332
333 context.visual.affected_files_cut_off = 60
333 context.visual.affected_files_cut_off = 60
334
334
335 context.pre_code = rc_config.get('rhodecode_pre_code')
335 context.pre_code = rc_config.get('rhodecode_pre_code')
336 context.post_code = rc_config.get('rhodecode_post_code')
336 context.post_code = rc_config.get('rhodecode_post_code')
337 context.rhodecode_name = rc_config.get('rhodecode_title')
337 context.rhodecode_name = rc_config.get('rhodecode_title')
338 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
338 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
339 # if we have specified default_encoding in the request, it has more
339 # if we have specified default_encoding in the request, it has more
340 # priority
340 # priority
341 if request.GET.get('default_encoding'):
341 if request.GET.get('default_encoding'):
342 context.default_encodings.insert(0, request.GET.get('default_encoding'))
342 context.default_encodings.insert(0, request.GET.get('default_encoding'))
343 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
343 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
344 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
344 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
345
345
346 # INI stored
346 # INI stored
347 context.labs_active = str2bool(
347 context.labs_active = str2bool(
348 config.get('labs_settings_active', 'false'))
348 config.get('labs_settings_active', 'false'))
349 context.ssh_enabled = str2bool(
349 context.ssh_enabled = str2bool(
350 config.get('ssh.generate_authorized_keyfile', 'false'))
350 config.get('ssh.generate_authorized_keyfile', 'false'))
351 context.ssh_key_generator_enabled = str2bool(
351 context.ssh_key_generator_enabled = str2bool(
352 config.get('ssh.enable_ui_key_generator', 'true'))
352 config.get('ssh.enable_ui_key_generator', 'true'))
353
353
354 context.visual.allow_repo_location_change = str2bool(
354 context.visual.allow_repo_location_change = str2bool(
355 config.get('allow_repo_location_change', True))
355 config.get('allow_repo_location_change', True))
356 context.visual.allow_custom_hooks_settings = str2bool(
356 context.visual.allow_custom_hooks_settings = str2bool(
357 config.get('allow_custom_hooks_settings', True))
357 config.get('allow_custom_hooks_settings', True))
358 context.debug_style = str2bool(config.get('debug_style', False))
358 context.debug_style = str2bool(config.get('debug_style', False))
359
359
360 context.rhodecode_instanceid = config.get('instance_id')
360 context.rhodecode_instanceid = config.get('instance_id')
361
361
362 context.visual.cut_off_limit_diff = safe_int(
362 context.visual.cut_off_limit_diff = safe_int(
363 config.get('cut_off_limit_diff'))
363 config.get('cut_off_limit_diff'))
364 context.visual.cut_off_limit_file = safe_int(
364 context.visual.cut_off_limit_file = safe_int(
365 config.get('cut_off_limit_file'))
365 config.get('cut_off_limit_file'))
366
366
367 context.license = AttributeDict({})
367 context.license = AttributeDict({})
368 context.license.hide_license_info = str2bool(
368 context.license.hide_license_info = str2bool(
369 config.get('license.hide_license_info', False))
369 config.get('license.hide_license_info', False))
370
370
371 # AppEnlight
371 # AppEnlight
372 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
372 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
373 context.appenlight_api_public_key = config.get(
373 context.appenlight_api_public_key = config.get(
374 'appenlight.api_public_key', '')
374 'appenlight.api_public_key', '')
375 context.appenlight_server_url = config.get('appenlight.server_url', '')
375 context.appenlight_server_url = config.get('appenlight.server_url', '')
376
376
377 diffmode = {
377 diffmode = {
378 "unified": "unified",
378 "unified": "unified",
379 "sideside": "sideside"
379 "sideside": "sideside"
380 }.get(request.GET.get('diffmode'))
380 }.get(request.GET.get('diffmode'))
381
381
382 if is_api is not None:
382 if is_api is not None:
383 is_api = hasattr(request, 'rpc_user')
383 is_api = hasattr(request, 'rpc_user')
384 session_attrs = {
384 session_attrs = {
385 # defaults
385 # defaults
386 "clone_url_format": "http",
386 "clone_url_format": "http",
387 "diffmode": "sideside"
387 "diffmode": "sideside",
388 "license_fingerprint": request.session.get('license_fingerprint')
388 }
389 }
389
390
390 if not is_api:
391 if not is_api:
391 # don't access pyramid session for API calls
392 # don't access pyramid session for API calls
392 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
393 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
393 request.session['rc_user_session_attr.diffmode'] = diffmode
394 request.session['rc_user_session_attr.diffmode'] = diffmode
394
395
395 # session settings per user
396 # session settings per user
396
397
397 for k, v in request.session.items():
398 for k, v in request.session.items():
398 pref = 'rc_user_session_attr.'
399 pref = 'rc_user_session_attr.'
399 if k and k.startswith(pref):
400 if k and k.startswith(pref):
400 k = k[len(pref):]
401 k = k[len(pref):]
401 session_attrs[k] = v
402 session_attrs[k] = v
402
403
403 context.user_session_attrs = session_attrs
404 context.user_session_attrs = session_attrs
404
405
405 # JS template context
406 # JS template context
406 context.template_context = {
407 context.template_context = {
407 'repo_name': None,
408 'repo_name': None,
408 'repo_type': None,
409 'repo_type': None,
409 'repo_landing_commit': None,
410 'repo_landing_commit': None,
410 'rhodecode_user': {
411 'rhodecode_user': {
411 'username': None,
412 'username': None,
412 'email': None,
413 'email': None,
413 'notification_status': False
414 'notification_status': False
414 },
415 },
415 'session_attrs': session_attrs,
416 'session_attrs': session_attrs,
416 'visual': {
417 'visual': {
417 'default_renderer': None
418 'default_renderer': None
418 },
419 },
419 'commit_data': {
420 'commit_data': {
420 'commit_id': None
421 'commit_id': None
421 },
422 },
422 'pull_request_data': {'pull_request_id': None},
423 'pull_request_data': {'pull_request_id': None},
423 'timeago': {
424 'timeago': {
424 'refresh_time': 120 * 1000,
425 'refresh_time': 120 * 1000,
425 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
426 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
426 },
427 },
427 'pyramid_dispatch': {
428 'pyramid_dispatch': {
428
429
429 },
430 },
430 'extra': {'plugins': {}}
431 'extra': {'plugins': {}}
431 }
432 }
432 # END CONFIG VARS
433 # END CONFIG VARS
433 if is_api:
434 if is_api:
434 csrf_token = None
435 csrf_token = None
435 else:
436 else:
436 csrf_token = auth.get_csrf_token(session=request.session)
437 csrf_token = auth.get_csrf_token(session=request.session)
437
438
438 context.csrf_token = csrf_token
439 context.csrf_token = csrf_token
439 context.backends = rhodecode.BACKENDS.keys()
440 context.backends = rhodecode.BACKENDS.keys()
440
441
441 unread_count = 0
442 unread_count = 0
442 user_bookmark_list = []
443 user_bookmark_list = []
443 if user_id:
444 if user_id:
444 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
445 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
445 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
446 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
446 context.unread_notifications = unread_count
447 context.unread_notifications = unread_count
447 context.bookmark_items = user_bookmark_list
448 context.bookmark_items = user_bookmark_list
448
449
449 # web case
450 # web case
450 if hasattr(request, 'user'):
451 if hasattr(request, 'user'):
451 context.auth_user = request.user
452 context.auth_user = request.user
452 context.rhodecode_user = request.user
453 context.rhodecode_user = request.user
453
454
454 # api case
455 # api case
455 if hasattr(request, 'rpc_user'):
456 if hasattr(request, 'rpc_user'):
456 context.auth_user = request.rpc_user
457 context.auth_user = request.rpc_user
457 context.rhodecode_user = request.rpc_user
458 context.rhodecode_user = request.rpc_user
458
459
459 # attach the whole call context to the request
460 # attach the whole call context to the request
460 request.call_context = context
461 request.call_context = context
461
462
462
463
463 def get_auth_user(request):
464 def get_auth_user(request):
464 environ = request.environ
465 environ = request.environ
465 session = request.session
466 session = request.session
466
467
467 ip_addr = get_ip_addr(environ)
468 ip_addr = get_ip_addr(environ)
468
469
469 # make sure that we update permissions each time we call controller
470 # make sure that we update permissions each time we call controller
470 _auth_token = (request.GET.get('auth_token', '') or request.GET.get('api_key', ''))
471 _auth_token = (request.GET.get('auth_token', '') or request.GET.get('api_key', ''))
471 if not _auth_token and request.matchdict:
472 if not _auth_token and request.matchdict:
472 url_auth_token = request.matchdict.get('_auth_token')
473 url_auth_token = request.matchdict.get('_auth_token')
473 _auth_token = url_auth_token
474 _auth_token = url_auth_token
474 if _auth_token:
475 if _auth_token:
475 log.debug('Using URL extracted auth token `...%s`', _auth_token[-4:])
476 log.debug('Using URL extracted auth token `...%s`', _auth_token[-4:])
476
477
477 if _auth_token:
478 if _auth_token:
478 # when using API_KEY we assume user exists, and
479 # when using API_KEY we assume user exists, and
479 # doesn't need auth based on cookies.
480 # doesn't need auth based on cookies.
480 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
481 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
481 authenticated = False
482 authenticated = False
482 else:
483 else:
483 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
484 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
484 try:
485 try:
485 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
486 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
486 ip_addr=ip_addr)
487 ip_addr=ip_addr)
487 except UserCreationError as e:
488 except UserCreationError as e:
488 h.flash(e, 'error')
489 h.flash(e, 'error')
489 # container auth or other auth functions that create users
490 # container auth or other auth functions that create users
490 # on the fly can throw this exception signaling that there's
491 # on the fly can throw this exception signaling that there's
491 # issue with user creation, explanation should be provided
492 # issue with user creation, explanation should be provided
492 # in Exception itself. We then create a simple blank
493 # in Exception itself. We then create a simple blank
493 # AuthUser
494 # AuthUser
494 auth_user = AuthUser(ip_addr=ip_addr)
495 auth_user = AuthUser(ip_addr=ip_addr)
495
496
496 # in case someone changes a password for user it triggers session
497 # in case someone changes a password for user it triggers session
497 # flush and forces a re-login
498 # flush and forces a re-login
498 if password_changed(auth_user, session):
499 if password_changed(auth_user, session):
499 session.invalidate()
500 session.invalidate()
500 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
501 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
501 auth_user = AuthUser(ip_addr=ip_addr)
502 auth_user = AuthUser(ip_addr=ip_addr)
502
503
503 authenticated = cookie_store.get('is_authenticated')
504 authenticated = cookie_store.get('is_authenticated')
504
505
505 if not auth_user.is_authenticated and auth_user.is_user_object:
506 if not auth_user.is_authenticated and auth_user.is_user_object:
506 # user is not authenticated and not empty
507 # user is not authenticated and not empty
507 auth_user.set_authenticated(authenticated)
508 auth_user.set_authenticated(authenticated)
508
509
509 return auth_user, _auth_token
510 return auth_user, _auth_token
510
511
511
512
512 def h_filter(s):
513 def h_filter(s):
513 """
514 """
514 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
515 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
515 we wrap this with additional functionality that converts None to empty
516 we wrap this with additional functionality that converts None to empty
516 strings
517 strings
517 """
518 """
518 if s is None:
519 if s is None:
519 return markupsafe.Markup()
520 return markupsafe.Markup()
520 return markupsafe.escape(s)
521 return markupsafe.escape(s)
521
522
522
523
523 def add_events_routes(config):
524 def add_events_routes(config):
524 """
525 """
525 Adds routing that can be used in events. Because some events are triggered
526 Adds routing that can be used in events. Because some events are triggered
526 outside of pyramid context, we need to bootstrap request with some
527 outside of pyramid context, we need to bootstrap request with some
527 routing registered
528 routing registered
528 """
529 """
529
530
530 from rhodecode.apps._base import ADMIN_PREFIX
531 from rhodecode.apps._base import ADMIN_PREFIX
531
532
532 config.add_route(name='home', pattern='/')
533 config.add_route(name='home', pattern='/')
533 config.add_route(name='main_page_repos_data', pattern='/_home_repos')
534 config.add_route(name='main_page_repos_data', pattern='/_home_repos')
534 config.add_route(name='main_page_repo_groups_data', pattern='/_home_repo_groups')
535 config.add_route(name='main_page_repo_groups_data', pattern='/_home_repo_groups')
535
536
536 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
537 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
537 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
538 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
538 config.add_route(name='repo_summary', pattern='/{repo_name}')
539 config.add_route(name='repo_summary', pattern='/{repo_name}')
539 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
540 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
540 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
541 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
541
542
542 config.add_route(name='pullrequest_show',
543 config.add_route(name='pullrequest_show',
543 pattern='/{repo_name}/pull-request/{pull_request_id}')
544 pattern='/{repo_name}/pull-request/{pull_request_id}')
544 config.add_route(name='pull_requests_global',
545 config.add_route(name='pull_requests_global',
545 pattern='/pull-request/{pull_request_id}')
546 pattern='/pull-request/{pull_request_id}')
546
547
547 config.add_route(name='repo_commit',
548 config.add_route(name='repo_commit',
548 pattern='/{repo_name}/changeset/{commit_id}')
549 pattern='/{repo_name}/changeset/{commit_id}')
549 config.add_route(name='repo_files',
550 config.add_route(name='repo_files',
550 pattern='/{repo_name}/files/{commit_id}/{f_path}')
551 pattern='/{repo_name}/files/{commit_id}/{f_path}')
551
552
552 config.add_route(name='hovercard_user',
553 config.add_route(name='hovercard_user',
553 pattern='/_hovercard/user/{user_id}')
554 pattern='/_hovercard/user/{user_id}')
554
555
555 config.add_route(name='hovercard_user_group',
556 config.add_route(name='hovercard_user_group',
556 pattern='/_hovercard/user_group/{user_group_id}')
557 pattern='/_hovercard/user_group/{user_group_id}')
557
558
558 config.add_route(name='hovercard_pull_request',
559 config.add_route(name='hovercard_pull_request',
559 pattern='/_hovercard/pull_request/{pull_request_id}')
560 pattern='/_hovercard/pull_request/{pull_request_id}')
560
561
561 config.add_route(name='hovercard_repo_commit',
562 config.add_route(name='hovercard_repo_commit',
562 pattern='/_hovercard/commit/{repo_name}/{commit_id}')
563 pattern='/_hovercard/commit/{repo_name}/{commit_id}')
563
564
564
565
565 def bootstrap_config(request):
566 def bootstrap_config(request):
566 import pyramid.testing
567 import pyramid.testing
567 registry = pyramid.testing.Registry('RcTestRegistry')
568 registry = pyramid.testing.Registry('RcTestRegistry')
568
569
569 config = pyramid.testing.setUp(registry=registry, request=request)
570 config = pyramid.testing.setUp(registry=registry, request=request)
570
571
571 # allow pyramid lookup in testing
572 # allow pyramid lookup in testing
572 config.include('pyramid_mako')
573 config.include('pyramid_mako')
573 config.include('rhodecode.lib.rc_beaker')
574 config.include('rhodecode.lib.rc_beaker')
574 config.include('rhodecode.lib.rc_cache')
575 config.include('rhodecode.lib.rc_cache')
575
576
576 add_events_routes(config)
577 add_events_routes(config)
577
578
578 return config
579 return config
579
580
580
581
581 def bootstrap_request(**kwargs):
582 def bootstrap_request(**kwargs):
582 import pyramid.testing
583 import pyramid.testing
583
584
584 class TestRequest(pyramid.testing.DummyRequest):
585 class TestRequest(pyramid.testing.DummyRequest):
585 application_url = kwargs.pop('application_url', 'http://example.com')
586 application_url = kwargs.pop('application_url', 'http://example.com')
586 host = kwargs.pop('host', 'example.com:80')
587 host = kwargs.pop('host', 'example.com:80')
587 domain = kwargs.pop('domain', 'example.com')
588 domain = kwargs.pop('domain', 'example.com')
588
589
589 def translate(self, msg):
590 def translate(self, msg):
590 return msg
591 return msg
591
592
592 def plularize(self, singular, plural, n):
593 def plularize(self, singular, plural, n):
593 return singular
594 return singular
594
595
595 def get_partial_renderer(self, tmpl_name):
596 def get_partial_renderer(self, tmpl_name):
596
597
597 from rhodecode.lib.partial_renderer import get_partial_renderer
598 from rhodecode.lib.partial_renderer import get_partial_renderer
598 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
599 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
599
600
600 _call_context = TemplateArgs()
601 _call_context = TemplateArgs()
601 _call_context.visual = TemplateArgs()
602 _call_context.visual = TemplateArgs()
602 _call_context.visual.show_sha_length = 12
603 _call_context.visual.show_sha_length = 12
603 _call_context.visual.show_revision_number = True
604 _call_context.visual.show_revision_number = True
604
605
605 @property
606 @property
606 def call_context(self):
607 def call_context(self):
607 return self._call_context
608 return self._call_context
608
609
609 class TestDummySession(pyramid.testing.DummySession):
610 class TestDummySession(pyramid.testing.DummySession):
610 def save(*arg, **kw):
611 def save(*arg, **kw):
611 pass
612 pass
612
613
613 request = TestRequest(**kwargs)
614 request = TestRequest(**kwargs)
614 request.session = TestDummySession()
615 request.session = TestDummySession()
615
616
616 return request
617 return request
617
618
@@ -1,81 +1,82 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2020 RhodeCode GmbH
3 # Copyright (C) 2015-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 from dogpile.cache import register_backend
22 from dogpile.cache import register_backend
23
23
24 register_backend(
24 register_backend(
25 "dogpile.cache.rc.memory_lru", "rhodecode.lib.rc_cache.backends",
25 "dogpile.cache.rc.memory_lru", "rhodecode.lib.rc_cache.backends",
26 "LRUMemoryBackend")
26 "LRUMemoryBackend")
27
27
28 register_backend(
28 register_backend(
29 "dogpile.cache.rc.file_namespace", "rhodecode.lib.rc_cache.backends",
29 "dogpile.cache.rc.file_namespace", "rhodecode.lib.rc_cache.backends",
30 "FileNamespaceBackend")
30 "FileNamespaceBackend")
31
31
32 register_backend(
32 register_backend(
33 "dogpile.cache.rc.redis", "rhodecode.lib.rc_cache.backends",
33 "dogpile.cache.rc.redis", "rhodecode.lib.rc_cache.backends",
34 "RedisPickleBackend")
34 "RedisPickleBackend")
35
35
36 register_backend(
36 register_backend(
37 "dogpile.cache.rc.redis_msgpack", "rhodecode.lib.rc_cache.backends",
37 "dogpile.cache.rc.redis_msgpack", "rhodecode.lib.rc_cache.backends",
38 "RedisMsgPackBackend")
38 "RedisMsgPackBackend")
39
39
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43 from . import region_meta
43 from . import region_meta
44 from .utils import (
44 from .utils import (
45 get_default_cache_settings, backend_key_generator, get_or_create_region,
45 get_default_cache_settings, backend_key_generator, get_or_create_region,
46 clear_cache_namespace, make_region, InvalidationContext,
46 clear_cache_namespace, make_region, InvalidationContext,
47 FreshRegionCache, ActiveRegionCache)
47 FreshRegionCache, ActiveRegionCache)
48
48
49
49
50 FILE_TREE_CACHE_VER = 'v4'
50 FILE_TREE_CACHE_VER = 'v4'
51 LICENSE_CACHE_VER = 'v2'
51
52
52
53
53 def configure_dogpile_cache(settings):
54 def configure_dogpile_cache(settings):
54 cache_dir = settings.get('cache_dir')
55 cache_dir = settings.get('cache_dir')
55 if cache_dir:
56 if cache_dir:
56 region_meta.dogpile_config_defaults['cache_dir'] = cache_dir
57 region_meta.dogpile_config_defaults['cache_dir'] = cache_dir
57
58
58 rc_cache_data = get_default_cache_settings(settings, prefixes=['rc_cache.'])
59 rc_cache_data = get_default_cache_settings(settings, prefixes=['rc_cache.'])
59
60
60 # inspect available namespaces
61 # inspect available namespaces
61 avail_regions = set()
62 avail_regions = set()
62 for key in rc_cache_data.keys():
63 for key in rc_cache_data.keys():
63 namespace_name = key.split('.', 1)[0]
64 namespace_name = key.split('.', 1)[0]
64 avail_regions.add(namespace_name)
65 avail_regions.add(namespace_name)
65 log.debug('dogpile: found following cache regions: %s', avail_regions)
66 log.debug('dogpile: found following cache regions: %s', avail_regions)
66
67
67 # register them into namespace
68 # register them into namespace
68 for region_name in avail_regions:
69 for region_name in avail_regions:
69 new_region = make_region(
70 new_region = make_region(
70 name=region_name,
71 name=region_name,
71 function_key_generator=None
72 function_key_generator=None
72 )
73 )
73
74
74 new_region.configure_from_config(settings, 'rc_cache.{}.'.format(region_name))
75 new_region.configure_from_config(settings, 'rc_cache.{}.'.format(region_name))
75 new_region.function_key_generator = backend_key_generator(new_region.actual_backend)
76 new_region.function_key_generator = backend_key_generator(new_region.actual_backend)
76 log.debug('dogpile: registering a new region %s[%s]', region_name, new_region.__dict__)
77 log.debug('dogpile: registering a new region %s[%s]', region_name, new_region.__dict__)
77 region_meta.dogpile_cache_regions[region_name] = new_region
78 region_meta.dogpile_cache_regions[region_name] = new_region
78
79
79
80
80 def includeme(config):
81 def includeme(config):
81 configure_dogpile_cache(config.registry.settings)
82 configure_dogpile_cache(config.registry.settings)
@@ -1,1208 +1,1221 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%!
3 <%!
4 from rhodecode.lib import html_filters
4 from rhodecode.lib import html_filters
5 %>
5 %>
6
6
7 <%inherit file="root.mako"/>
7 <%inherit file="root.mako"/>
8
8
9 <%include file="/ejs_templates/templates.html"/>
9 <%include file="/ejs_templates/templates.html"/>
10
10
11 <div class="outerwrapper">
11 <div class="outerwrapper">
12 <!-- HEADER -->
12 <!-- HEADER -->
13 <div class="header">
13 <div class="header">
14 <div id="header-inner" class="wrapper">
14 <div id="header-inner" class="wrapper">
15 <div id="logo">
15 <div id="logo">
16 <div class="logo-wrapper">
16 <div class="logo-wrapper">
17 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
17 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
18 </div>
18 </div>
19 % if c.rhodecode_name:
19 % if c.rhodecode_name:
20 <div class="branding">
20 <div class="branding">
21 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
21 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
22 </div>
22 </div>
23 % endif
23 % endif
24 </div>
24 </div>
25 <!-- MENU BAR NAV -->
25 <!-- MENU BAR NAV -->
26 ${self.menu_bar_nav()}
26 ${self.menu_bar_nav()}
27 <!-- END MENU BAR NAV -->
27 <!-- END MENU BAR NAV -->
28 </div>
28 </div>
29 </div>
29 </div>
30 ${self.menu_bar_subnav()}
30 ${self.menu_bar_subnav()}
31 <!-- END HEADER -->
31 <!-- END HEADER -->
32
32
33 <!-- CONTENT -->
33 <!-- CONTENT -->
34 <div id="content" class="wrapper">
34 <div id="content" class="wrapper">
35
35
36 <rhodecode-toast id="notifications"></rhodecode-toast>
36 <rhodecode-toast id="notifications"></rhodecode-toast>
37
37
38 <div class="main">
38 <div class="main">
39 ${next.main()}
39 ${next.main()}
40 </div>
40 </div>
41 </div>
41 </div>
42 <!-- END CONTENT -->
42 <!-- END CONTENT -->
43
43
44 </div>
44 </div>
45 <!-- FOOTER -->
45 <!-- FOOTER -->
46 <div id="footer">
46 <div id="footer">
47 <div id="footer-inner" class="title wrapper">
47 <div id="footer-inner" class="title wrapper">
48 <div>
48 <div>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50
50
51 <p class="footer-link-right">
51 <p class="footer-link-right">
52 <a class="grey-link-action" href="${h.route_path('home', _query={'showrcid': 1})}">
52 <a class="grey-link-action" href="${h.route_path('home', _query={'showrcid': 1})}">
53 RhodeCode
53 RhodeCode
54 % if c.visual.show_version:
54 % if c.visual.show_version:
55 ${c.rhodecode_version}
55 ${c.rhodecode_version}
56 % endif
56 % endif
57 ${c.rhodecode_edition}
57 ${c.rhodecode_edition}
58 </a> |
58 </a> |
59
59
60 % if c.visual.rhodecode_support_url:
60 % if c.visual.rhodecode_support_url:
61 <a class="grey-link-action" href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a> |
61 <a class="grey-link-action" href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a> |
62 <a class="grey-link-action" href="https://docs.rhodecode.com" target="_blank">${_('Documentation')}</a>
62 <a class="grey-link-action" href="https://docs.rhodecode.com" target="_blank">${_('Documentation')}</a>
63 % endif
63 % endif
64
64
65 </p>
65 </p>
66
66
67 <p class="server-instance" style="display:${sid}">
67 <p class="server-instance" style="display:${sid}">
68 ## display hidden instance ID if specially defined
68 ## display hidden instance ID if specially defined
69 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
69 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
70 % if c.rhodecode_instanceid:
70 % if c.rhodecode_instanceid:
71 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
71 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
72 % endif
72 % endif
73 </p>
73 </p>
74 </div>
74 </div>
75 </div>
75 </div>
76 </div>
76 </div>
77
77
78 <!-- END FOOTER -->
78 <!-- END FOOTER -->
79
79
80 ### MAKO DEFS ###
80 ### MAKO DEFS ###
81
81
82 <%def name="menu_bar_subnav()">
82 <%def name="menu_bar_subnav()">
83 </%def>
83 </%def>
84
84
85 <%def name="breadcrumbs(class_='breadcrumbs')">
85 <%def name="breadcrumbs(class_='breadcrumbs')">
86 <div class="${class_}">
86 <div class="${class_}">
87 ${self.breadcrumbs_links()}
87 ${self.breadcrumbs_links()}
88 </div>
88 </div>
89 </%def>
89 </%def>
90
90
91 <%def name="admin_menu(active=None)">
91 <%def name="admin_menu(active=None)">
92
92
93 <div id="context-bar">
93 <div id="context-bar">
94 <div class="wrapper">
94 <div class="wrapper">
95 <div class="title">
95 <div class="title">
96 <div class="title-content">
96 <div class="title-content">
97 <div class="title-main">
97 <div class="title-main">
98 % if c.is_super_admin:
98 % if c.is_super_admin:
99 ${_('Super-admin Panel')}
99 ${_('Super-admin Panel')}
100 % else:
100 % else:
101 ${_('Delegated Admin Panel')}
101 ${_('Delegated Admin Panel')}
102 % endif
102 % endif
103 </div>
103 </div>
104 </div>
104 </div>
105 </div>
105 </div>
106
106
107 <ul id="context-pages" class="navigation horizontal-list">
107 <ul id="context-pages" class="navigation horizontal-list">
108
108
109 ## super-admin case
109 ## super-admin case
110 % if c.is_super_admin:
110 % if c.is_super_admin:
111 <li class="${h.is_active('audit_logs', active)}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
111 <li class="${h.is_active('audit_logs', active)}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
112 <li class="${h.is_active('repositories', active)}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
112 <li class="${h.is_active('repositories', active)}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
113 <li class="${h.is_active('repository_groups', active)}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
113 <li class="${h.is_active('repository_groups', active)}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
114 <li class="${h.is_active('users', active)}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
114 <li class="${h.is_active('users', active)}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
115 <li class="${h.is_active('user_groups', active)}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
115 <li class="${h.is_active('user_groups', active)}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
116 <li class="${h.is_active('permissions', active)}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
116 <li class="${h.is_active('permissions', active)}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
117 <li class="${h.is_active('authentication', active)}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
117 <li class="${h.is_active('authentication', active)}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
118 <li class="${h.is_active('integrations', active)}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
118 <li class="${h.is_active('integrations', active)}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
119 <li class="${h.is_active('defaults', active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
119 <li class="${h.is_active('defaults', active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
120 <li class="${h.is_active('settings', active)}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
120 <li class="${h.is_active('settings', active)}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
121
121
122 ## delegated admin
122 ## delegated admin
123 % elif c.is_delegated_admin:
123 % elif c.is_delegated_admin:
124 <%
124 <%
125 repositories=c.auth_user.repositories_admin or c.can_create_repo
125 repositories=c.auth_user.repositories_admin or c.can_create_repo
126 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
126 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
127 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
127 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
128 %>
128 %>
129
129
130 %if repositories:
130 %if repositories:
131 <li class="${h.is_active('repositories', active)} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
131 <li class="${h.is_active('repositories', active)} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
132 %endif
132 %endif
133 %if repository_groups:
133 %if repository_groups:
134 <li class="${h.is_active('repository_groups', active)} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
134 <li class="${h.is_active('repository_groups', active)} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
135 %endif
135 %endif
136 %if user_groups:
136 %if user_groups:
137 <li class="${h.is_active('user_groups', active)} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
137 <li class="${h.is_active('user_groups', active)} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
138 %endif
138 %endif
139 % endif
139 % endif
140 </ul>
140 </ul>
141
141
142 </div>
142 </div>
143 <div class="clear"></div>
143 <div class="clear"></div>
144 </div>
144 </div>
145 </%def>
145 </%def>
146
146
147 <%def name="dt_info_panel(elements)">
147 <%def name="dt_info_panel(elements)">
148 <dl class="dl-horizontal">
148 <dl class="dl-horizontal">
149 %for dt, dd, title, show_items in elements:
149 %for dt, dd, title, show_items in elements:
150 <dt>${dt}:</dt>
150 <dt>${dt}:</dt>
151 <dd title="${h.tooltip(title)}">
151 <dd title="${h.tooltip(title)}">
152 %if callable(dd):
152 %if callable(dd):
153 ## allow lazy evaluation of elements
153 ## allow lazy evaluation of elements
154 ${dd()}
154 ${dd()}
155 %else:
155 %else:
156 ${dd}
156 ${dd}
157 %endif
157 %endif
158 %if show_items:
158 %if show_items:
159 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
159 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
160 %endif
160 %endif
161 </dd>
161 </dd>
162
162
163 %if show_items:
163 %if show_items:
164 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
164 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
165 %for item in show_items:
165 %for item in show_items:
166 <dt></dt>
166 <dt></dt>
167 <dd>${item}</dd>
167 <dd>${item}</dd>
168 %endfor
168 %endfor
169 </div>
169 </div>
170 %endif
170 %endif
171
171
172 %endfor
172 %endfor
173 </dl>
173 </dl>
174 </%def>
174 </%def>
175
175
176 <%def name="tr_info_entry(element)">
176 <%def name="tr_info_entry(element)">
177 <% key, val, title, show_items = element %>
177 <% key, val, title, show_items = element %>
178
178
179 <tr>
179 <tr>
180 <td style="vertical-align: top">${key}</td>
180 <td style="vertical-align: top">${key}</td>
181 <td title="${h.tooltip(title)}">
181 <td title="${h.tooltip(title)}">
182 %if callable(val):
182 %if callable(val):
183 ## allow lazy evaluation of elements
183 ## allow lazy evaluation of elements
184 ${val()}
184 ${val()}
185 %else:
185 %else:
186 ${val}
186 ${val}
187 %endif
187 %endif
188 %if show_items:
188 %if show_items:
189 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
189 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
190 % for item in show_items:
190 % for item in show_items:
191 <dt></dt>
191 <dt></dt>
192 <dd>${item}</dd>
192 <dd>${item}</dd>
193 % endfor
193 % endfor
194 </div>
194 </div>
195 %endif
195 %endif
196 </td>
196 </td>
197 <td style="vertical-align: top">
197 <td style="vertical-align: top">
198 %if show_items:
198 %if show_items:
199 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
199 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
200 %endif
200 %endif
201 </td>
201 </td>
202 </tr>
202 </tr>
203
203
204 </%def>
204 </%def>
205
205
206 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None, extra_class=None)">
206 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None, extra_class=None)">
207 <%
207 <%
208 if size > 16:
208 if size > 16:
209 gravatar_class = ['gravatar','gravatar-large']
209 gravatar_class = ['gravatar','gravatar-large']
210 else:
210 else:
211 gravatar_class = ['gravatar']
211 gravatar_class = ['gravatar']
212
212
213 data_hovercard_url = ''
213 data_hovercard_url = ''
214 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
214 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
215
215
216 if tooltip:
216 if tooltip:
217 gravatar_class += ['tooltip-hovercard']
217 gravatar_class += ['tooltip-hovercard']
218 if extra_class:
218 if extra_class:
219 gravatar_class += extra_class
219 gravatar_class += extra_class
220 if tooltip and user:
220 if tooltip and user:
221 if user.username == h.DEFAULT_USER:
221 if user.username == h.DEFAULT_USER:
222 gravatar_class.pop(-1)
222 gravatar_class.pop(-1)
223 else:
223 else:
224 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
224 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
225 gravatar_class = ' '.join(gravatar_class)
225 gravatar_class = ' '.join(gravatar_class)
226
226
227 %>
227 %>
228 <%doc>
228 <%doc>
229 TODO: johbo: For now we serve double size images to make it smooth
229 TODO: johbo: For now we serve double size images to make it smooth
230 for retina. This is how it worked until now. Should be replaced
230 for retina. This is how it worked until now. Should be replaced
231 with a better solution at some point.
231 with a better solution at some point.
232 </%doc>
232 </%doc>
233
233
234 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
234 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
235 </%def>
235 </%def>
236
236
237
237
238 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False, _class='rc-user')">
238 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False, _class='rc-user')">
239 <%
239 <%
240 email = h.email_or_none(contact)
240 email = h.email_or_none(contact)
241 rc_user = h.discover_user(contact)
241 rc_user = h.discover_user(contact)
242 %>
242 %>
243
243
244 <div class="${_class}">
244 <div class="${_class}">
245 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
245 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
246 <span class="${('user user-disabled' if show_disabled else 'user')}">
246 <span class="${('user user-disabled' if show_disabled else 'user')}">
247 ${h.link_to_user(rc_user or contact)}
247 ${h.link_to_user(rc_user or contact)}
248 </span>
248 </span>
249 </div>
249 </div>
250 </%def>
250 </%def>
251
251
252
252
253 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
253 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
254 <%
254 <%
255 if (size > 16):
255 if (size > 16):
256 gravatar_class = 'icon-user-group-alt'
256 gravatar_class = 'icon-user-group-alt'
257 else:
257 else:
258 gravatar_class = 'icon-user-group-alt'
258 gravatar_class = 'icon-user-group-alt'
259
259
260 if tooltip:
260 if tooltip:
261 gravatar_class += ' tooltip-hovercard'
261 gravatar_class += ' tooltip-hovercard'
262
262
263 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
263 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
264 %>
264 %>
265 <%doc>
265 <%doc>
266 TODO: johbo: For now we serve double size images to make it smooth
266 TODO: johbo: For now we serve double size images to make it smooth
267 for retina. This is how it worked until now. Should be replaced
267 for retina. This is how it worked until now. Should be replaced
268 with a better solution at some point.
268 with a better solution at some point.
269 </%doc>
269 </%doc>
270
270
271 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
271 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
272 </%def>
272 </%def>
273
273
274 <%def name="repo_page_title(repo_instance)">
274 <%def name="repo_page_title(repo_instance)">
275 <div class="title-content repo-title">
275 <div class="title-content repo-title">
276
276
277 <div class="title-main">
277 <div class="title-main">
278 ## SVN/HG/GIT icons
278 ## SVN/HG/GIT icons
279 %if h.is_hg(repo_instance):
279 %if h.is_hg(repo_instance):
280 <i class="icon-hg"></i>
280 <i class="icon-hg"></i>
281 %endif
281 %endif
282 %if h.is_git(repo_instance):
282 %if h.is_git(repo_instance):
283 <i class="icon-git"></i>
283 <i class="icon-git"></i>
284 %endif
284 %endif
285 %if h.is_svn(repo_instance):
285 %if h.is_svn(repo_instance):
286 <i class="icon-svn"></i>
286 <i class="icon-svn"></i>
287 %endif
287 %endif
288
288
289 ## public/private
289 ## public/private
290 %if repo_instance.private:
290 %if repo_instance.private:
291 <i class="icon-repo-private"></i>
291 <i class="icon-repo-private"></i>
292 %else:
292 %else:
293 <i class="icon-repo-public"></i>
293 <i class="icon-repo-public"></i>
294 %endif
294 %endif
295
295
296 ## repo name with group name
296 ## repo name with group name
297 ${h.breadcrumb_repo_link(repo_instance)}
297 ${h.breadcrumb_repo_link(repo_instance)}
298
298
299 ## Context Actions
299 ## Context Actions
300 <div class="pull-right">
300 <div class="pull-right">
301 %if c.rhodecode_user.username != h.DEFAULT_USER:
301 %if c.rhodecode_user.username != h.DEFAULT_USER:
302 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
302 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
303
303
304 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
304 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
305 % if c.repository_is_user_following:
305 % if c.repository_is_user_following:
306 <i class="icon-eye-off"></i>${_('Unwatch')}
306 <i class="icon-eye-off"></i>${_('Unwatch')}
307 % else:
307 % else:
308 <i class="icon-eye"></i>${_('Watch')}
308 <i class="icon-eye"></i>${_('Watch')}
309 % endif
309 % endif
310
310
311 </a>
311 </a>
312 %else:
312 %else:
313 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
313 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
314 %endif
314 %endif
315 </div>
315 </div>
316
316
317 </div>
317 </div>
318
318
319 ## FORKED
319 ## FORKED
320 %if repo_instance.fork:
320 %if repo_instance.fork:
321 <p class="discreet">
321 <p class="discreet">
322 <i class="icon-code-fork"></i> ${_('Fork of')}
322 <i class="icon-code-fork"></i> ${_('Fork of')}
323 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
323 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
324 </p>
324 </p>
325 %endif
325 %endif
326
326
327 ## IMPORTED FROM REMOTE
327 ## IMPORTED FROM REMOTE
328 %if repo_instance.clone_uri:
328 %if repo_instance.clone_uri:
329 <p class="discreet">
329 <p class="discreet">
330 <i class="icon-code-fork"></i> ${_('Clone from')}
330 <i class="icon-code-fork"></i> ${_('Clone from')}
331 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
331 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
332 </p>
332 </p>
333 %endif
333 %endif
334
334
335 ## LOCKING STATUS
335 ## LOCKING STATUS
336 %if repo_instance.locked[0]:
336 %if repo_instance.locked[0]:
337 <p class="locking_locked discreet">
337 <p class="locking_locked discreet">
338 <i class="icon-repo-lock"></i>
338 <i class="icon-repo-lock"></i>
339 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
339 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
340 </p>
340 </p>
341 %elif repo_instance.enable_locking:
341 %elif repo_instance.enable_locking:
342 <p class="locking_unlocked discreet">
342 <p class="locking_unlocked discreet">
343 <i class="icon-repo-unlock"></i>
343 <i class="icon-repo-unlock"></i>
344 ${_('Repository not locked. Pull repository to lock it.')}
344 ${_('Repository not locked. Pull repository to lock it.')}
345 </p>
345 </p>
346 %endif
346 %endif
347
347
348 </div>
348 </div>
349 </%def>
349 </%def>
350
350
351 <%def name="repo_menu(active=None)">
351 <%def name="repo_menu(active=None)">
352 <%
352 <%
353 ## determine if we have "any" option available
353 ## determine if we have "any" option available
354 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
354 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
355 has_actions = can_lock
355 has_actions = can_lock
356
356
357 %>
357 %>
358 % if c.rhodecode_db_repo.archived:
358 % if c.rhodecode_db_repo.archived:
359 <div class="alert alert-warning text-center">
359 <div class="alert alert-warning text-center">
360 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
360 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
361 </div>
361 </div>
362 % endif
362 % endif
363
363
364 <!--- REPO CONTEXT BAR -->
364 <!--- REPO CONTEXT BAR -->
365 <div id="context-bar">
365 <div id="context-bar">
366 <div class="wrapper">
366 <div class="wrapper">
367
367
368 <div class="title">
368 <div class="title">
369 ${self.repo_page_title(c.rhodecode_db_repo)}
369 ${self.repo_page_title(c.rhodecode_db_repo)}
370 </div>
370 </div>
371
371
372 <ul id="context-pages" class="navigation horizontal-list">
372 <ul id="context-pages" class="navigation horizontal-list">
373 <li class="${h.is_active('summary', active)}"><a class="menulink" href="${h.route_path('repo_summary_explicit', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
373 <li class="${h.is_active('summary', active)}"><a class="menulink" href="${h.route_path('repo_summary_explicit', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
374 <li class="${h.is_active('commits', active)}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
374 <li class="${h.is_active('commits', active)}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
375 <li class="${h.is_active('files', active)}"><a class="menulink" href="${h.repo_files_by_ref_url(c.repo_name, c.rhodecode_db_repo.repo_type, f_path='', ref_name=c.rhodecode_db_repo.landing_ref_name, commit_id='tip', query={'at':c.rhodecode_db_repo.landing_ref_name})}"><div class="menulabel">${_('Files')}</div></a></li>
375 <li class="${h.is_active('files', active)}"><a class="menulink" href="${h.repo_files_by_ref_url(c.repo_name, c.rhodecode_db_repo.repo_type, f_path='', ref_name=c.rhodecode_db_repo.landing_ref_name, commit_id='tip', query={'at':c.rhodecode_db_repo.landing_ref_name})}"><div class="menulabel">${_('Files')}</div></a></li>
376 <li class="${h.is_active('compare', active)}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
376 <li class="${h.is_active('compare', active)}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
377
377
378 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
378 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
379 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
379 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
380 <li class="${h.is_active('showpullrequest', active)}">
380 <li class="${h.is_active('showpullrequest', active)}">
381 <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)}">
381 <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)}">
382 <div class="menulabel">
382 <div class="menulabel">
383 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
383 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
384 </div>
384 </div>
385 </a>
385 </a>
386 </li>
386 </li>
387 %endif
387 %endif
388
388
389 <li class="${h.is_active('artifacts', active)}">
389 <li class="${h.is_active('artifacts', active)}">
390 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
390 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
391 <div class="menulabel">
391 <div class="menulabel">
392 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
392 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
393 </div>
393 </div>
394 </a>
394 </a>
395 </li>
395 </li>
396
396
397 %if not c.rhodecode_db_repo.archived and h.HasRepoPermissionAll('repository.admin')(c.repo_name):
397 %if not c.rhodecode_db_repo.archived and h.HasRepoPermissionAll('repository.admin')(c.repo_name):
398 <li class="${h.is_active('settings', active)}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
398 <li class="${h.is_active('settings', active)}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
399 %endif
399 %endif
400
400
401 <li class="${h.is_active('options', active)}">
401 <li class="${h.is_active('options', active)}">
402 % if has_actions:
402 % if has_actions:
403 <a class="menulink dropdown">
403 <a class="menulink dropdown">
404 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
404 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
405 </a>
405 </a>
406 <ul class="submenu">
406 <ul class="submenu">
407 %if can_lock:
407 %if can_lock:
408 %if c.rhodecode_db_repo.locked[0]:
408 %if c.rhodecode_db_repo.locked[0]:
409 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
409 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
410 %else:
410 %else:
411 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
411 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
412 %endif
412 %endif
413 %endif
413 %endif
414 </ul>
414 </ul>
415 % endif
415 % endif
416 </li>
416 </li>
417
417
418 </ul>
418 </ul>
419 </div>
419 </div>
420 <div class="clear"></div>
420 <div class="clear"></div>
421 </div>
421 </div>
422
422
423 <!--- REPO END CONTEXT BAR -->
423 <!--- REPO END CONTEXT BAR -->
424
424
425 </%def>
425 </%def>
426
426
427 <%def name="repo_group_page_title(repo_group_instance)">
427 <%def name="repo_group_page_title(repo_group_instance)">
428 <div class="title-content">
428 <div class="title-content">
429 <div class="title-main">
429 <div class="title-main">
430 ## Repository Group icon
430 ## Repository Group icon
431 <i class="icon-repo-group"></i>
431 <i class="icon-repo-group"></i>
432
432
433 ## repo name with group name
433 ## repo name with group name
434 ${h.breadcrumb_repo_group_link(repo_group_instance)}
434 ${h.breadcrumb_repo_group_link(repo_group_instance)}
435 </div>
435 </div>
436
436
437 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
437 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
438 <div class="repo-group-desc discreet">
438 <div class="repo-group-desc discreet">
439 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
439 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
440 </div>
440 </div>
441
441
442 </div>
442 </div>
443 </%def>
443 </%def>
444
444
445
445
446 <%def name="repo_group_menu(active=None)">
446 <%def name="repo_group_menu(active=None)">
447 <%
447 <%
448 gr_name = c.repo_group.group_name if c.repo_group else None
448 gr_name = c.repo_group.group_name if c.repo_group else None
449 # create repositories with write permission on group is set to true
449 # create repositories with write permission on group is set to true
450 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
450 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
451
451
452 %>
452 %>
453
453
454
454
455 <!--- REPO GROUP CONTEXT BAR -->
455 <!--- REPO GROUP CONTEXT BAR -->
456 <div id="context-bar">
456 <div id="context-bar">
457 <div class="wrapper">
457 <div class="wrapper">
458 <div class="title">
458 <div class="title">
459 ${self.repo_group_page_title(c.repo_group)}
459 ${self.repo_group_page_title(c.repo_group)}
460 </div>
460 </div>
461
461
462 <ul id="context-pages" class="navigation horizontal-list">
462 <ul id="context-pages" class="navigation horizontal-list">
463 <li class="${h.is_active('home', active)}">
463 <li class="${h.is_active('home', active)}">
464 <a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a>
464 <a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a>
465 </li>
465 </li>
466 % if c.is_super_admin or group_admin:
466 % if c.is_super_admin or group_admin:
467 <li class="${h.is_active('settings', active)}">
467 <li class="${h.is_active('settings', active)}">
468 <a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a>
468 <a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a>
469 </li>
469 </li>
470 % endif
470 % endif
471
471
472 </ul>
472 </ul>
473 </div>
473 </div>
474 <div class="clear"></div>
474 <div class="clear"></div>
475 </div>
475 </div>
476
476
477 <!--- REPO GROUP CONTEXT BAR -->
477 <!--- REPO GROUP CONTEXT BAR -->
478
478
479 </%def>
479 </%def>
480
480
481
481
482 <%def name="usermenu(active=False)">
482 <%def name="usermenu(active=False)">
483 <%
483 <%
484 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
484 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
485
485
486 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
486 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
487 # create repositories with write permission on group is set to true
487 # create repositories with write permission on group is set to true
488
488
489 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
489 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
490 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
490 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
491 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
491 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
492 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
492 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
493
493
494 can_create_repos = c.is_super_admin or c.can_create_repo
494 can_create_repos = c.is_super_admin or c.can_create_repo
495 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
495 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
496
496
497 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
497 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
498 can_create_repo_groups_in_group = c.is_super_admin or group_admin
498 can_create_repo_groups_in_group = c.is_super_admin or group_admin
499 %>
499 %>
500
500
501 % if not_anonymous:
501 % if not_anonymous:
502 <%
502 <%
503 default_target_group = dict()
503 default_target_group = dict()
504 if c.rhodecode_user.personal_repo_group:
504 if c.rhodecode_user.personal_repo_group:
505 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
505 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
506 %>
506 %>
507
507
508 ## create action
508 ## create action
509 <li>
509 <li>
510 <a href="#create-actions" onclick="return false;" class="menulink childs">
510 <a href="#create-actions" onclick="return false;" class="menulink childs">
511 <i class="icon-plus-circled"></i>
511 <i class="icon-plus-circled"></i>
512 </a>
512 </a>
513
513
514 <div class="action-menu submenu">
514 <div class="action-menu submenu">
515
515
516 <ol>
516 <ol>
517 ## scope of within a repository
517 ## scope of within a repository
518 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
518 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
519 <li class="submenu-title">${_('This Repository')}</li>
519 <li class="submenu-title">${_('This Repository')}</li>
520 <li>
520 <li>
521 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
521 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
522 </li>
522 </li>
523 % if can_fork:
523 % if can_fork:
524 <li>
524 <li>
525 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
525 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
526 </li>
526 </li>
527 % endif
527 % endif
528 % endif
528 % endif
529
529
530 ## scope of within repository groups
530 ## scope of within repository groups
531 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
531 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
532 <li class="submenu-title">${_('This Repository Group')}</li>
532 <li class="submenu-title">${_('This Repository Group')}</li>
533
533
534 % if can_create_repos_in_group:
534 % if can_create_repos_in_group:
535 <li>
535 <li>
536 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('New Repository')}</a>
536 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('New Repository')}</a>
537 </li>
537 </li>
538 % endif
538 % endif
539
539
540 % if can_create_repo_groups_in_group:
540 % if can_create_repo_groups_in_group:
541 <li>
541 <li>
542 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'New Repository Group')}</a>
542 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'New Repository Group')}</a>
543 </li>
543 </li>
544 % endif
544 % endif
545 % endif
545 % endif
546
546
547 ## personal group
547 ## personal group
548 % if c.rhodecode_user.personal_repo_group:
548 % if c.rhodecode_user.personal_repo_group:
549 <li class="submenu-title">Personal Group</li>
549 <li class="submenu-title">Personal Group</li>
550
550
551 <li>
551 <li>
552 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
552 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
553 </li>
553 </li>
554
554
555 <li>
555 <li>
556 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
556 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
557 </li>
557 </li>
558 % endif
558 % endif
559
559
560 ## Global actions
560 ## Global actions
561 <li class="submenu-title">RhodeCode</li>
561 <li class="submenu-title">RhodeCode</li>
562 % if can_create_repos:
562 % if can_create_repos:
563 <li>
563 <li>
564 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
564 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
565 </li>
565 </li>
566 % endif
566 % endif
567
567
568 % if can_create_repo_groups:
568 % if can_create_repo_groups:
569 <li>
569 <li>
570 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
570 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
571 </li>
571 </li>
572 % endif
572 % endif
573
573
574 <li>
574 <li>
575 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
575 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
576 </li>
576 </li>
577
577
578 </ol>
578 </ol>
579
579
580 </div>
580 </div>
581 </li>
581 </li>
582
582
583 ## notifications
583 ## notifications
584 <li>
584 <li>
585 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
585 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
586 ${c.unread_notifications}
586 ${c.unread_notifications}
587 </a>
587 </a>
588 </li>
588 </li>
589 % endif
589 % endif
590
590
591 ## USER MENU
591 ## USER MENU
592 <li id="quick_login_li" class="${'active' if active else ''}">
592 <li id="quick_login_li" class="${'active' if active else ''}">
593 % if c.rhodecode_user.username == h.DEFAULT_USER:
593 % if c.rhodecode_user.username == h.DEFAULT_USER:
594 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
594 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
595 ${gravatar(c.rhodecode_user.email, 20)}
595 ${gravatar(c.rhodecode_user.email, 20)}
596 <span class="user">
596 <span class="user">
597 <span>${_('Sign in')}</span>
597 <span>${_('Sign in')}</span>
598 </span>
598 </span>
599 </a>
599 </a>
600 % else:
600 % else:
601 ## logged in user
601 ## logged in user
602 <a id="quick_login_link" class="menulink childs">
602 <a id="quick_login_link" class="menulink childs">
603 ${gravatar(c.rhodecode_user.email, 20)}
603 ${gravatar(c.rhodecode_user.email, 20)}
604 <span class="user">
604 <span class="user">
605 <span class="menu_link_user">${c.rhodecode_user.username}</span>
605 <span class="menu_link_user">${c.rhodecode_user.username}</span>
606 <div class="show_more"></div>
606 <div class="show_more"></div>
607 </span>
607 </span>
608 </a>
608 </a>
609 ## subnav with menu for logged in user
609 ## subnav with menu for logged in user
610 <div class="user-menu submenu">
610 <div class="user-menu submenu">
611 <div id="quick_login">
611 <div id="quick_login">
612 %if c.rhodecode_user.username != h.DEFAULT_USER:
612 %if c.rhodecode_user.username != h.DEFAULT_USER:
613 <div class="">
613 <div class="">
614 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
614 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
615 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
615 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
616 <div class="email">${c.rhodecode_user.email}</div>
616 <div class="email">${c.rhodecode_user.email}</div>
617 </div>
617 </div>
618 <div class="">
618 <div class="">
619 <ol class="links">
619 <ol class="links">
620 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
620 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
621 % if c.rhodecode_user.personal_repo_group:
621 % if c.rhodecode_user.personal_repo_group:
622 <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>
622 <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>
623 % endif
623 % endif
624 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
624 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
625
625
626 % if c.debug_style:
626 % if c.debug_style:
627 <li>
627 <li>
628 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
628 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
629 <div class="menulabel">${_('[Style]')}</div>
629 <div class="menulabel">${_('[Style]')}</div>
630 </a>
630 </a>
631 </li>
631 </li>
632 % endif
632 % endif
633
633
634 ## bookmark-items
634 ## bookmark-items
635 <li class="bookmark-items">
635 <li class="bookmark-items">
636 ${_('Bookmarks')}
636 ${_('Bookmarks')}
637 <div class="pull-right">
637 <div class="pull-right">
638 <a href="${h.route_path('my_account_bookmarks')}">
638 <a href="${h.route_path('my_account_bookmarks')}">
639
639
640 <i class="icon-cog"></i>
640 <i class="icon-cog"></i>
641 </a>
641 </a>
642 </div>
642 </div>
643 </li>
643 </li>
644 % if not c.bookmark_items:
644 % if not c.bookmark_items:
645 <li>
645 <li>
646 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
646 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
647 </li>
647 </li>
648 % endif
648 % endif
649 % for item in c.bookmark_items:
649 % for item in c.bookmark_items:
650 <li>
650 <li>
651 % if item.repository:
651 % if item.repository:
652 <div>
652 <div>
653 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
653 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
654 <code>${item.position}</code>
654 <code>${item.position}</code>
655 % if item.repository.repo_type == 'hg':
655 % if item.repository.repo_type == 'hg':
656 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
656 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
657 % elif item.repository.repo_type == 'git':
657 % elif item.repository.repo_type == 'git':
658 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
658 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
659 % elif item.repository.repo_type == 'svn':
659 % elif item.repository.repo_type == 'svn':
660 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
660 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
661 % endif
661 % endif
662 ${(item.title or h.shorter(item.repository.repo_name, 30))}
662 ${(item.title or h.shorter(item.repository.repo_name, 30))}
663 </a>
663 </a>
664 </div>
664 </div>
665 % elif item.repository_group:
665 % elif item.repository_group:
666 <div>
666 <div>
667 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
667 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
668 <code>${item.position}</code>
668 <code>${item.position}</code>
669 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
669 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
670 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
670 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
671 </a>
671 </a>
672 </div>
672 </div>
673 % else:
673 % else:
674 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
674 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
675 <code>${item.position}</code>
675 <code>${item.position}</code>
676 ${item.title}
676 ${item.title}
677 </a>
677 </a>
678 % endif
678 % endif
679 </li>
679 </li>
680 % endfor
680 % endfor
681
681
682 <li class="logout">
682 <li class="logout">
683 ${h.secure_form(h.route_path('logout'), request=request)}
683 ${h.secure_form(h.route_path('logout'), request=request)}
684 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
684 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
685 ${h.end_form()}
685 ${h.end_form()}
686 </li>
686 </li>
687 </ol>
687 </ol>
688 </div>
688 </div>
689 %endif
689 %endif
690 </div>
690 </div>
691 </div>
691 </div>
692
692
693 % endif
693 % endif
694 </li>
694 </li>
695 </%def>
695 </%def>
696
696
697 <%def name="menu_items(active=None)">
697 <%def name="menu_items(active=None)">
698 <%
698 <%
699 notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
699 notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
700 notice_display = 'none' if len(notice_messages) == 0 else ''
700 notice_display = 'none' if len(notice_messages) == 0 else ''
701 %>
701 %>
702 <style>
702 <style>
703
703
704 </style>
704 </style>
705
705
706 <ul id="quick" class="main_nav navigation horizontal-list">
706 <ul id="quick" class="main_nav navigation horizontal-list">
707 ## notice box for important system messages
707 ## notice box for important system messages
708 <li style="display: ${notice_display}">
708 <li style="display: ${notice_display}">
709 <a class="notice-box" href="#openNotice" onclick="$('.notice-messages-container').toggle(); return false">
709 <a class="notice-box" href="#openNotice" onclick="$('.notice-messages-container').toggle(); return false">
710 <div class="menulabel-notice ${notice_level}" >
710 <div class="menulabel-notice ${notice_level}" >
711 ${len(notice_messages)}
711 ${len(notice_messages)}
712 </div>
712 </div>
713 </a>
713 </a>
714 </li>
714 </li>
715 <div class="notice-messages-container" style="display: none">
715 <div class="notice-messages-container" style="display: none">
716 <div class="notice-messages">
716 <div class="notice-messages">
717 <table class="rctable">
717 <table class="rctable">
718 % for notice in notice_messages:
718 % for notice in notice_messages:
719 <tr id="notice-message-${notice['msg_id']}" class="notice-message-${notice['level']}">
719 <tr id="notice-message-${notice['msg_id']}" class="notice-message-${notice['level']}">
720 <td style="vertical-align: text-top; width: 20px">
720 <td style="vertical-align: text-top; width: 20px">
721 <i class="tooltip icon-info notice-color-${notice['level']}" title="${notice['level']}"></i>
721 <i class="tooltip icon-info notice-color-${notice['level']}" title="${notice['level']}"></i>
722 </td>
722 </td>
723 <td>
723 <td>
724 <span><i class="icon-plus-squared cursor-pointer" onclick="$('#notice-${notice['msg_id']}').toggle()"></i> </span>
724 <span><i class="icon-plus-squared cursor-pointer" onclick="$('#notice-${notice['msg_id']}').toggle()"></i> </span>
725 ${notice['subject']}
725 ${notice['subject']}
726
726
727 <div id="notice-${notice['msg_id']}" style="display: none">
727 <div id="notice-${notice['msg_id']}" style="display: none">
728 ${h.render(notice['body'], renderer='markdown')}
728 ${h.render(notice['body'], renderer='markdown')}
729 </div>
729 </div>
730 </td>
730 </td>
731 <td style="vertical-align: text-top; width: 35px;">
731 <td style="vertical-align: text-top; width: 35px;">
732 <a class="tooltip" title="${_('dismiss')}" href="#dismiss" onclick="dismissNotice(${notice['msg_id']});return false">
732 <a class="tooltip" title="${_('dismiss')}" href="#dismiss" onclick="dismissNotice(${notice['msg_id']});return false">
733 <i class="icon-remove icon-filled-red"></i>
733 <i class="icon-remove icon-filled-red"></i>
734 </a>
734 </a>
735 </td>
735 </td>
736 </tr>
736 </tr>
737
737
738 % endfor
738 % endfor
739 </table>
739 </table>
740 </div>
740 </div>
741 </div>
741 </div>
742 ## Main filter
742 ## Main filter
743 <li>
743 <li>
744 <div class="menulabel main_filter_box">
744 <div class="menulabel main_filter_box">
745 <div class="main_filter_input_box">
745 <div class="main_filter_input_box">
746 <ul class="searchItems">
746 <ul class="searchItems">
747
747
748 <li class="searchTag searchTagIcon">
748 <li class="searchTag searchTagIcon">
749 <i class="icon-search"></i>
749 <i class="icon-search"></i>
750 </li>
750 </li>
751
751
752 % if c.template_context['search_context']['repo_id']:
752 % if c.template_context['search_context']['repo_id']:
753 <li class="searchTag searchTagFilter searchTagHidable" >
753 <li class="searchTag searchTagFilter searchTagHidable" >
754 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
754 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
755 <span class="tag">
755 <span class="tag">
756 This repo
756 This repo
757 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
757 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
758 </span>
758 </span>
759 ##</a>
759 ##</a>
760 </li>
760 </li>
761 % elif c.template_context['search_context']['repo_group_id']:
761 % elif c.template_context['search_context']['repo_group_id']:
762 <li class="searchTag searchTagFilter searchTagHidable">
762 <li class="searchTag searchTagFilter searchTagHidable">
763 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
763 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
764 <span class="tag">
764 <span class="tag">
765 This group
765 This group
766 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
766 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
767 </span>
767 </span>
768 ##</a>
768 ##</a>
769 </li>
769 </li>
770 % endif
770 % endif
771
771
772 <li class="searchTagInput">
772 <li class="searchTagInput">
773 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
773 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
774 </li>
774 </li>
775 <li class="searchTag searchTagHelp">
775 <li class="searchTag searchTagHelp">
776 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
776 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
777 </li>
777 </li>
778 </ul>
778 </ul>
779 </div>
779 </div>
780 </div>
780 </div>
781
781
782 <div id="main_filter_help" style="display: none">
782 <div id="main_filter_help" style="display: none">
783 - Use '/' key to quickly access this field.
783 - Use '/' key to quickly access this field.
784
784
785 - Enter a name of repository, or repository group for quick search.
785 - Enter a name of repository, or repository group for quick search.
786
786
787 - Prefix query to allow special search:
787 - Prefix query to allow special search:
788
788
789 user:admin, to search for usernames, always global
789 user:admin, to search for usernames, always global
790
790
791 user_group:devops, to search for user groups, always global
791 user_group:devops, to search for user groups, always global
792
792
793 pr:303, to search for pull request number, title, or description, always global
793 pr:303, to search for pull request number, title, or description, always global
794
794
795 commit:efced4, to search for commits, scoped to repositories or groups
795 commit:efced4, to search for commits, scoped to repositories or groups
796
796
797 file:models.py, to search for file paths, scoped to repositories or groups
797 file:models.py, to search for file paths, scoped to repositories or groups
798
798
799 % if c.template_context['search_context']['repo_id']:
799 % if c.template_context['search_context']['repo_id']:
800 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
800 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
801 % elif c.template_context['search_context']['repo_group_id']:
801 % elif c.template_context['search_context']['repo_group_id']:
802 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
802 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
803 % else:
803 % else:
804 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
804 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
805 % endif
805 % endif
806 </div>
806 </div>
807 </li>
807 </li>
808
808
809 ## ROOT MENU
809 ## ROOT MENU
810 <li class="${h.is_active('home', active)}">
810 <li class="${h.is_active('home', active)}">
811 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
811 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
812 <div class="menulabel">${_('Home')}</div>
812 <div class="menulabel">${_('Home')}</div>
813 </a>
813 </a>
814 </li>
814 </li>
815
815
816 %if c.rhodecode_user.username != h.DEFAULT_USER:
816 %if c.rhodecode_user.username != h.DEFAULT_USER:
817 <li class="${h.is_active('journal', active)}">
817 <li class="${h.is_active('journal', active)}">
818 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
818 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
819 <div class="menulabel">${_('Journal')}</div>
819 <div class="menulabel">${_('Journal')}</div>
820 </a>
820 </a>
821 </li>
821 </li>
822 %else:
822 %else:
823 <li class="${h.is_active('journal', active)}">
823 <li class="${h.is_active('journal', active)}">
824 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
824 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
825 <div class="menulabel">${_('Public journal')}</div>
825 <div class="menulabel">${_('Public journal')}</div>
826 </a>
826 </a>
827 </li>
827 </li>
828 %endif
828 %endif
829
829
830 <li class="${h.is_active('gists', active)}">
830 <li class="${h.is_active('gists', active)}">
831 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
831 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
832 <div class="menulabel">${_('Gists')}</div>
832 <div class="menulabel">${_('Gists')}</div>
833 </a>
833 </a>
834 </li>
834 </li>
835
835
836 % if c.is_super_admin or c.is_delegated_admin:
836 % if c.is_super_admin or c.is_delegated_admin:
837 <li class="${h.is_active('admin', active)}">
837 <li class="${h.is_active('admin', active)}">
838 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
838 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
839 <div class="menulabel">${_('Admin')} </div>
839 <div class="menulabel">${_('Admin')} </div>
840 </a>
840 </a>
841 </li>
841 </li>
842 % endif
842 % endif
843
843
844 ## render extra user menu
844 ## render extra user menu
845 ${usermenu(active=(active=='my_account'))}
845 ${usermenu(active=(active=='my_account'))}
846
846
847 </ul>
847 </ul>
848
848
849 <script type="text/javascript">
849 <script type="text/javascript">
850 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
850 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
851
851
852 var formatRepoResult = function(result, container, query, escapeMarkup) {
852 var formatRepoResult = function(result, container, query, escapeMarkup) {
853 return function(data, escapeMarkup) {
853 return function(data, escapeMarkup) {
854 if (!data.repo_id){
854 if (!data.repo_id){
855 return data.text; // optgroup text Repositories
855 return data.text; // optgroup text Repositories
856 }
856 }
857
857
858 var tmpl = '';
858 var tmpl = '';
859 var repoType = data['repo_type'];
859 var repoType = data['repo_type'];
860 var repoName = data['text'];
860 var repoName = data['text'];
861
861
862 if(data && data.type == 'repo'){
862 if(data && data.type == 'repo'){
863 if(repoType === 'hg'){
863 if(repoType === 'hg'){
864 tmpl += '<i class="icon-hg"></i> ';
864 tmpl += '<i class="icon-hg"></i> ';
865 }
865 }
866 else if(repoType === 'git'){
866 else if(repoType === 'git'){
867 tmpl += '<i class="icon-git"></i> ';
867 tmpl += '<i class="icon-git"></i> ';
868 }
868 }
869 else if(repoType === 'svn'){
869 else if(repoType === 'svn'){
870 tmpl += '<i class="icon-svn"></i> ';
870 tmpl += '<i class="icon-svn"></i> ';
871 }
871 }
872 if(data['private']){
872 if(data['private']){
873 tmpl += '<i class="icon-lock" ></i> ';
873 tmpl += '<i class="icon-lock" ></i> ';
874 }
874 }
875 else if(visualShowPublicIcon){
875 else if(visualShowPublicIcon){
876 tmpl += '<i class="icon-unlock-alt"></i> ';
876 tmpl += '<i class="icon-unlock-alt"></i> ';
877 }
877 }
878 }
878 }
879 tmpl += escapeMarkup(repoName);
879 tmpl += escapeMarkup(repoName);
880 return tmpl;
880 return tmpl;
881
881
882 }(result, escapeMarkup);
882 }(result, escapeMarkup);
883 };
883 };
884
884
885 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
885 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
886 return function(data, escapeMarkup) {
886 return function(data, escapeMarkup) {
887 if (!data.repo_group_id){
887 if (!data.repo_group_id){
888 return data.text; // optgroup text Repositories
888 return data.text; // optgroup text Repositories
889 }
889 }
890
890
891 var tmpl = '';
891 var tmpl = '';
892 var repoGroupName = data['text'];
892 var repoGroupName = data['text'];
893
893
894 if(data){
894 if(data){
895
895
896 tmpl += '<i class="icon-repo-group"></i> ';
896 tmpl += '<i class="icon-repo-group"></i> ';
897
897
898 }
898 }
899 tmpl += escapeMarkup(repoGroupName);
899 tmpl += escapeMarkup(repoGroupName);
900 return tmpl;
900 return tmpl;
901
901
902 }(result, escapeMarkup);
902 }(result, escapeMarkup);
903 };
903 };
904
904
905 var escapeRegExChars = function (value) {
905 var escapeRegExChars = function (value) {
906 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
906 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
907 };
907 };
908
908
909 var getRepoIcon = function(repo_type) {
909 var getRepoIcon = function(repo_type) {
910 if (repo_type === 'hg') {
910 if (repo_type === 'hg') {
911 return '<i class="icon-hg"></i> ';
911 return '<i class="icon-hg"></i> ';
912 }
912 }
913 else if (repo_type === 'git') {
913 else if (repo_type === 'git') {
914 return '<i class="icon-git"></i> ';
914 return '<i class="icon-git"></i> ';
915 }
915 }
916 else if (repo_type === 'svn') {
916 else if (repo_type === 'svn') {
917 return '<i class="icon-svn"></i> ';
917 return '<i class="icon-svn"></i> ';
918 }
918 }
919 return ''
919 return ''
920 };
920 };
921
921
922 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
922 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
923
923
924 if (value.split(':').length === 2) {
924 if (value.split(':').length === 2) {
925 value = value.split(':')[1]
925 value = value.split(':')[1]
926 }
926 }
927
927
928 var searchType = data['type'];
928 var searchType = data['type'];
929 var searchSubType = data['subtype'];
929 var searchSubType = data['subtype'];
930 var valueDisplay = data['value_display'];
930 var valueDisplay = data['value_display'];
931 var valueIcon = data['value_icon'];
931 var valueIcon = data['value_icon'];
932
932
933 var pattern = '(' + escapeRegExChars(value) + ')';
933 var pattern = '(' + escapeRegExChars(value) + ')';
934
934
935 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
935 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
936
936
937 // highlight match
937 // highlight match
938 if (searchType != 'text') {
938 if (searchType != 'text') {
939 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
939 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
940 }
940 }
941
941
942 var icon = '';
942 var icon = '';
943
943
944 if (searchType === 'hint') {
944 if (searchType === 'hint') {
945 icon += '<i class="icon-repo-group"></i> ';
945 icon += '<i class="icon-repo-group"></i> ';
946 }
946 }
947 // full text search/hints
947 // full text search/hints
948 else if (searchType === 'search') {
948 else if (searchType === 'search') {
949 if (valueIcon === undefined) {
949 if (valueIcon === undefined) {
950 icon += '<i class="icon-more"></i> ';
950 icon += '<i class="icon-more"></i> ';
951 } else {
951 } else {
952 icon += valueIcon + ' ';
952 icon += valueIcon + ' ';
953 }
953 }
954
954
955 if (searchSubType !== undefined && searchSubType == 'repo') {
955 if (searchSubType !== undefined && searchSubType == 'repo') {
956 valueDisplay += '<div class="pull-right tag">repository</div>';
956 valueDisplay += '<div class="pull-right tag">repository</div>';
957 }
957 }
958 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
958 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
959 valueDisplay += '<div class="pull-right tag">repo group</div>';
959 valueDisplay += '<div class="pull-right tag">repo group</div>';
960 }
960 }
961 }
961 }
962 // repository
962 // repository
963 else if (searchType === 'repo') {
963 else if (searchType === 'repo') {
964
964
965 var repoIcon = getRepoIcon(data['repo_type']);
965 var repoIcon = getRepoIcon(data['repo_type']);
966 icon += repoIcon;
966 icon += repoIcon;
967
967
968 if (data['private']) {
968 if (data['private']) {
969 icon += '<i class="icon-lock" ></i> ';
969 icon += '<i class="icon-lock" ></i> ';
970 }
970 }
971 else if (visualShowPublicIcon) {
971 else if (visualShowPublicIcon) {
972 icon += '<i class="icon-unlock-alt"></i> ';
972 icon += '<i class="icon-unlock-alt"></i> ';
973 }
973 }
974 }
974 }
975 // repository groups
975 // repository groups
976 else if (searchType === 'repo_group') {
976 else if (searchType === 'repo_group') {
977 icon += '<i class="icon-repo-group"></i> ';
977 icon += '<i class="icon-repo-group"></i> ';
978 }
978 }
979 // user group
979 // user group
980 else if (searchType === 'user_group') {
980 else if (searchType === 'user_group') {
981 icon += '<i class="icon-group"></i> ';
981 icon += '<i class="icon-group"></i> ';
982 }
982 }
983 // user
983 // user
984 else if (searchType === 'user') {
984 else if (searchType === 'user') {
985 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
985 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
986 }
986 }
987 // pull request
987 // pull request
988 else if (searchType === 'pull_request') {
988 else if (searchType === 'pull_request') {
989 icon += '<i class="icon-merge"></i> ';
989 icon += '<i class="icon-merge"></i> ';
990 }
990 }
991 // commit
991 // commit
992 else if (searchType === 'commit') {
992 else if (searchType === 'commit') {
993 var repo_data = data['repo_data'];
993 var repo_data = data['repo_data'];
994 var repoIcon = getRepoIcon(repo_data['repository_type']);
994 var repoIcon = getRepoIcon(repo_data['repository_type']);
995 if (repoIcon) {
995 if (repoIcon) {
996 icon += repoIcon;
996 icon += repoIcon;
997 } else {
997 } else {
998 icon += '<i class="icon-tag"></i>';
998 icon += '<i class="icon-tag"></i>';
999 }
999 }
1000 }
1000 }
1001 // file
1001 // file
1002 else if (searchType === 'file') {
1002 else if (searchType === 'file') {
1003 var repo_data = data['repo_data'];
1003 var repo_data = data['repo_data'];
1004 var repoIcon = getRepoIcon(repo_data['repository_type']);
1004 var repoIcon = getRepoIcon(repo_data['repository_type']);
1005 if (repoIcon) {
1005 if (repoIcon) {
1006 icon += repoIcon;
1006 icon += repoIcon;
1007 } else {
1007 } else {
1008 icon += '<i class="icon-tag"></i>';
1008 icon += '<i class="icon-tag"></i>';
1009 }
1009 }
1010 }
1010 }
1011 // generic text
1011 // generic text
1012 else if (searchType === 'text') {
1012 else if (searchType === 'text') {
1013 icon = '';
1013 icon = '';
1014 }
1014 }
1015
1015
1016 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
1016 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
1017 return tmpl.format(icon, valueDisplay);
1017 return tmpl.format(icon, valueDisplay);
1018 };
1018 };
1019
1019
1020 var handleSelect = function(element, suggestion) {
1020 var handleSelect = function(element, suggestion) {
1021 if (suggestion.type === "hint") {
1021 if (suggestion.type === "hint") {
1022 // we skip action
1022 // we skip action
1023 $('#main_filter').focus();
1023 $('#main_filter').focus();
1024 }
1024 }
1025 else if (suggestion.type === "text") {
1025 else if (suggestion.type === "text") {
1026 // we skip action
1026 // we skip action
1027 $('#main_filter').focus();
1027 $('#main_filter').focus();
1028
1028
1029 } else {
1029 } else {
1030 window.location = suggestion['url'];
1030 window.location = suggestion['url'];
1031 }
1031 }
1032 };
1032 };
1033
1033
1034 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
1034 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
1035 if (queryLowerCase.split(':').length === 2) {
1035 if (queryLowerCase.split(':').length === 2) {
1036 queryLowerCase = queryLowerCase.split(':')[1]
1036 queryLowerCase = queryLowerCase.split(':')[1]
1037 }
1037 }
1038 if (suggestion.type === "text") {
1038 if (suggestion.type === "text") {
1039 // special case we don't want to "skip" display for
1039 // special case we don't want to "skip" display for
1040 return true
1040 return true
1041 }
1041 }
1042 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
1042 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
1043 };
1043 };
1044
1044
1045 var cleanContext = {
1045 var cleanContext = {
1046 repo_view_type: null,
1046 repo_view_type: null,
1047
1047
1048 repo_id: null,
1048 repo_id: null,
1049 repo_name: "",
1049 repo_name: "",
1050
1050
1051 repo_group_id: null,
1051 repo_group_id: null,
1052 repo_group_name: null
1052 repo_group_name: null
1053 };
1053 };
1054 var removeGoToFilter = function () {
1054 var removeGoToFilter = function () {
1055 $('.searchTagHidable').hide();
1055 $('.searchTagHidable').hide();
1056 $('#main_filter').autocomplete(
1056 $('#main_filter').autocomplete(
1057 'setOptions', {params:{search_context: cleanContext}});
1057 'setOptions', {params:{search_context: cleanContext}});
1058 };
1058 };
1059
1059
1060 $('#main_filter').autocomplete({
1060 $('#main_filter').autocomplete({
1061 serviceUrl: pyroutes.url('goto_switcher_data'),
1061 serviceUrl: pyroutes.url('goto_switcher_data'),
1062 params: {
1062 params: {
1063 "search_context": templateContext.search_context
1063 "search_context": templateContext.search_context
1064 },
1064 },
1065 minChars:2,
1065 minChars:2,
1066 maxHeight:400,
1066 maxHeight:400,
1067 deferRequestBy: 300, //miliseconds
1067 deferRequestBy: 300, //miliseconds
1068 tabDisabled: true,
1068 tabDisabled: true,
1069 autoSelectFirst: false,
1069 autoSelectFirst: false,
1070 containerClass: 'autocomplete-qfilter-suggestions',
1070 containerClass: 'autocomplete-qfilter-suggestions',
1071 formatResult: autocompleteMainFilterFormatResult,
1071 formatResult: autocompleteMainFilterFormatResult,
1072 lookupFilter: autocompleteMainFilterResult,
1072 lookupFilter: autocompleteMainFilterResult,
1073 onSelect: function (element, suggestion) {
1073 onSelect: function (element, suggestion) {
1074 handleSelect(element, suggestion);
1074 handleSelect(element, suggestion);
1075 return false;
1075 return false;
1076 },
1076 },
1077 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1077 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1078 if (jqXHR !== 'abort') {
1078 if (jqXHR !== 'abort') {
1079 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
1079 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
1080 SwalNoAnimation.fire({
1080 SwalNoAnimation.fire({
1081 icon: 'error',
1081 icon: 'error',
1082 title: _gettext('Error during search operation'),
1082 title: _gettext('Error during search operation'),
1083 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
1083 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
1084 }).then(function(result) {
1084 }).then(function(result) {
1085 window.location.reload();
1085 window.location.reload();
1086 })
1086 })
1087 }
1087 }
1088 },
1088 },
1089 onSearchStart: function (params) {
1089 onSearchStart: function (params) {
1090 $('.searchTag.searchTagIcon').html('<i class="icon-spin animate-spin"></i>')
1090 $('.searchTag.searchTagIcon').html('<i class="icon-spin animate-spin"></i>')
1091 },
1091 },
1092 onSearchComplete: function (query, suggestions) {
1092 onSearchComplete: function (query, suggestions) {
1093 $('.searchTag.searchTagIcon').html('<i class="icon-search"></i>')
1093 $('.searchTag.searchTagIcon').html('<i class="icon-search"></i>')
1094 },
1094 },
1095 });
1095 });
1096
1096
1097 showMainFilterBox = function () {
1097 showMainFilterBox = function () {
1098 $('#main_filter_help').toggle();
1098 $('#main_filter_help').toggle();
1099 };
1099 };
1100
1100
1101 $('#main_filter').on('keydown.autocomplete', function (e) {
1101 $('#main_filter').on('keydown.autocomplete', function (e) {
1102
1102
1103 var BACKSPACE = 8;
1103 var BACKSPACE = 8;
1104 var el = $(e.currentTarget);
1104 var el = $(e.currentTarget);
1105 if(e.which === BACKSPACE){
1105 if(e.which === BACKSPACE){
1106 var inputVal = el.val();
1106 var inputVal = el.val();
1107 if (inputVal === ""){
1107 if (inputVal === ""){
1108 removeGoToFilter()
1108 removeGoToFilter()
1109 }
1109 }
1110 }
1110 }
1111 });
1111 });
1112
1112
1113 var dismissNotice = function(noticeId) {
1113 var dismissNotice = function(noticeId) {
1114
1114
1115 var url = pyroutes.url('user_notice_dismiss',
1115 var url = pyroutes.url('user_notice_dismiss',
1116 {"user_id": templateContext.rhodecode_user.user_id});
1116 {"user_id": templateContext.rhodecode_user.user_id});
1117
1117
1118 var postData = {
1118 var postData = {
1119 'csrf_token': CSRF_TOKEN,
1119 'csrf_token': CSRF_TOKEN,
1120 'notice_id': noticeId,
1120 'notice_id': noticeId,
1121 };
1121 };
1122
1122
1123 var success = function(response) {
1123 var success = function(response) {
1124 $('#notice-message-' + noticeId).remove();
1124 $('#notice-message-' + noticeId).remove();
1125 return false;
1125 return false;
1126 };
1126 };
1127 var failure = function(data, textStatus, xhr) {
1127 var failure = function(data, textStatus, xhr) {
1128 alert("error processing request: " + textStatus);
1128 alert("error processing request: " + textStatus);
1129 return false;
1129 return false;
1130 };
1130 };
1131 ajaxPOST(url, postData, success, failure);
1131 ajaxPOST(url, postData, success, failure);
1132 }
1132 }
1133
1134 var hideLicenseWarning = function () {
1135 var fingerprint = templateContext.session_attrs.license_fingerprint;
1136 storeUserSessionAttr('rc_user_session_attr.hide_license_warning', fingerprint);
1137 $('#notifications').hide();
1138 }
1139
1140 var hideLicenseError = function () {
1141 var fingerprint = templateContext.session_attrs.license_fingerprint;
1142 storeUserSessionAttr('rc_user_session_attr.hide_license_error', fingerprint);
1143 $('#notifications').hide();
1144 }
1145
1133 </script>
1146 </script>
1134 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1147 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1135 </%def>
1148 </%def>
1136
1149
1137 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1150 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1138 <div class="modal-dialog">
1151 <div class="modal-dialog">
1139 <div class="modal-content">
1152 <div class="modal-content">
1140 <div class="modal-header">
1153 <div class="modal-header">
1141 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1154 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1142 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1155 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1143 </div>
1156 </div>
1144 <div class="modal-body">
1157 <div class="modal-body">
1145 <div class="block-left">
1158 <div class="block-left">
1146 <table class="keyboard-mappings">
1159 <table class="keyboard-mappings">
1147 <tbody>
1160 <tbody>
1148 <tr>
1161 <tr>
1149 <th></th>
1162 <th></th>
1150 <th>${_('Site-wide shortcuts')}</th>
1163 <th>${_('Site-wide shortcuts')}</th>
1151 </tr>
1164 </tr>
1152 <%
1165 <%
1153 elems = [
1166 elems = [
1154 ('/', 'Use quick search box'),
1167 ('/', 'Use quick search box'),
1155 ('g h', 'Goto home page'),
1168 ('g h', 'Goto home page'),
1156 ('g g', 'Goto my private gists page'),
1169 ('g g', 'Goto my private gists page'),
1157 ('g G', 'Goto my public gists page'),
1170 ('g G', 'Goto my public gists page'),
1158 ('g 0-9', 'Goto bookmarked items from 0-9'),
1171 ('g 0-9', 'Goto bookmarked items from 0-9'),
1159 ('n r', 'New repository page'),
1172 ('n r', 'New repository page'),
1160 ('n g', 'New gist page'),
1173 ('n g', 'New gist page'),
1161 ]
1174 ]
1162 %>
1175 %>
1163 %for key, desc in elems:
1176 %for key, desc in elems:
1164 <tr>
1177 <tr>
1165 <td class="keys">
1178 <td class="keys">
1166 <span class="key tag">${key}</span>
1179 <span class="key tag">${key}</span>
1167 </td>
1180 </td>
1168 <td>${desc}</td>
1181 <td>${desc}</td>
1169 </tr>
1182 </tr>
1170 %endfor
1183 %endfor
1171 </tbody>
1184 </tbody>
1172 </table>
1185 </table>
1173 </div>
1186 </div>
1174 <div class="block-left">
1187 <div class="block-left">
1175 <table class="keyboard-mappings">
1188 <table class="keyboard-mappings">
1176 <tbody>
1189 <tbody>
1177 <tr>
1190 <tr>
1178 <th></th>
1191 <th></th>
1179 <th>${_('Repositories')}</th>
1192 <th>${_('Repositories')}</th>
1180 </tr>
1193 </tr>
1181 <%
1194 <%
1182 elems = [
1195 elems = [
1183 ('g s', 'Goto summary page'),
1196 ('g s', 'Goto summary page'),
1184 ('g c', 'Goto changelog page'),
1197 ('g c', 'Goto changelog page'),
1185 ('g f', 'Goto files page'),
1198 ('g f', 'Goto files page'),
1186 ('g F', 'Goto files page with file search activated'),
1199 ('g F', 'Goto files page with file search activated'),
1187 ('g p', 'Goto pull requests page'),
1200 ('g p', 'Goto pull requests page'),
1188 ('g o', 'Goto repository settings'),
1201 ('g o', 'Goto repository settings'),
1189 ('g O', 'Goto repository access permissions settings'),
1202 ('g O', 'Goto repository access permissions settings'),
1190 ]
1203 ]
1191 %>
1204 %>
1192 %for key, desc in elems:
1205 %for key, desc in elems:
1193 <tr>
1206 <tr>
1194 <td class="keys">
1207 <td class="keys">
1195 <span class="key tag">${key}</span>
1208 <span class="key tag">${key}</span>
1196 </td>
1209 </td>
1197 <td>${desc}</td>
1210 <td>${desc}</td>
1198 </tr>
1211 </tr>
1199 %endfor
1212 %endfor
1200 </tbody>
1213 </tbody>
1201 </table>
1214 </table>
1202 </div>
1215 </div>
1203 </div>
1216 </div>
1204 <div class="modal-footer">
1217 <div class="modal-footer">
1205 </div>
1218 </div>
1206 </div><!-- /.modal-content -->
1219 </div><!-- /.modal-content -->
1207 </div><!-- /.modal-dialog -->
1220 </div><!-- /.modal-dialog -->
1208 </div><!-- /.modal -->
1221 </div><!-- /.modal -->
General Comments 0
You need to be logged in to leave comments. Login now