##// END OF EJS Templates
pyramid: fix problem with default language for users without any explicit one set.
marcink -
r1919:4f4ecf65 default
parent child Browse files
Show More
@@ -1,632 +1,632 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 The base Controller API
23 23 Provides the BaseController class for subclassing. And usage in different
24 24 controllers
25 25 """
26 26
27 27 import logging
28 28 import socket
29 29
30 30 import ipaddress
31 31 import pyramid.threadlocal
32 32
33 33 from paste.auth.basic import AuthBasicAuthenticator
34 34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36 36 from pylons import config, tmpl_context as c, request, url
37 37 from pylons.controllers import WSGIController
38 38 from pylons.controllers.util import redirect
39 39 from pylons.i18n import translation
40 40 # marcink: don't remove this import
41 41 from pylons.templating import render_mako as render # noqa
42 42 from pylons.i18n.translation import _
43 43 from webob.exc import HTTPFound
44 44
45 45
46 46 import rhodecode
47 47 from rhodecode.authentication.base import VCS_TYPE
48 48 from rhodecode.lib import auth, utils2
49 49 from rhodecode.lib import helpers as h
50 50 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
51 51 from rhodecode.lib.exceptions import UserCreationError
52 52 from rhodecode.lib.utils import (
53 53 get_repo_slug, set_rhodecode_config, password_changed,
54 54 get_enabled_hook_classes)
55 55 from rhodecode.lib.utils2 import (
56 56 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist)
57 57 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
58 58 from rhodecode.model import meta
59 59 from rhodecode.model.db import Repository, User, ChangesetComment
60 60 from rhodecode.model.notification import NotificationModel
61 61 from rhodecode.model.scm import ScmModel
62 62 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
63 63
64 64
65 65 log = logging.getLogger(__name__)
66 66
67 67
68 68 def _filter_proxy(ip):
69 69 """
70 70 Passed in IP addresses in HEADERS can be in a special format of multiple
71 71 ips. Those comma separated IPs are passed from various proxies in the
72 72 chain of request processing. The left-most being the original client.
73 73 We only care about the first IP which came from the org. client.
74 74
75 75 :param ip: ip string from headers
76 76 """
77 77 if ',' in ip:
78 78 _ips = ip.split(',')
79 79 _first_ip = _ips[0].strip()
80 80 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
81 81 return _first_ip
82 82 return ip
83 83
84 84
85 85 def _filter_port(ip):
86 86 """
87 87 Removes a port from ip, there are 4 main cases to handle here.
88 88 - ipv4 eg. 127.0.0.1
89 89 - ipv6 eg. ::1
90 90 - ipv4+port eg. 127.0.0.1:8080
91 91 - ipv6+port eg. [::1]:8080
92 92
93 93 :param ip:
94 94 """
95 95 def is_ipv6(ip_addr):
96 96 if hasattr(socket, 'inet_pton'):
97 97 try:
98 98 socket.inet_pton(socket.AF_INET6, ip_addr)
99 99 except socket.error:
100 100 return False
101 101 else:
102 102 # fallback to ipaddress
103 103 try:
104 104 ipaddress.IPv6Address(safe_unicode(ip_addr))
105 105 except Exception:
106 106 return False
107 107 return True
108 108
109 109 if ':' not in ip: # must be ipv4 pure ip
110 110 return ip
111 111
112 112 if '[' in ip and ']' in ip: # ipv6 with port
113 113 return ip.split(']')[0][1:].lower()
114 114
115 115 # must be ipv6 or ipv4 with port
116 116 if is_ipv6(ip):
117 117 return ip
118 118 else:
119 119 ip, _port = ip.split(':')[:2] # means ipv4+port
120 120 return ip
121 121
122 122
123 123 def get_ip_addr(environ):
124 124 proxy_key = 'HTTP_X_REAL_IP'
125 125 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
126 126 def_key = 'REMOTE_ADDR'
127 127 _filters = lambda x: _filter_port(_filter_proxy(x))
128 128
129 129 ip = environ.get(proxy_key)
130 130 if ip:
131 131 return _filters(ip)
132 132
133 133 ip = environ.get(proxy_key2)
134 134 if ip:
135 135 return _filters(ip)
136 136
137 137 ip = environ.get(def_key, '0.0.0.0')
138 138 return _filters(ip)
139 139
140 140
141 141 def get_server_ip_addr(environ, log_errors=True):
142 142 hostname = environ.get('SERVER_NAME')
143 143 try:
144 144 return socket.gethostbyname(hostname)
145 145 except Exception as e:
146 146 if log_errors:
147 147 # in some cases this lookup is not possible, and we don't want to
148 148 # make it an exception in logs
149 149 log.exception('Could not retrieve server ip address: %s', e)
150 150 return hostname
151 151
152 152
153 153 def get_server_port(environ):
154 154 return environ.get('SERVER_PORT')
155 155
156 156
157 157 def get_access_path(environ):
158 158 path = environ.get('PATH_INFO')
159 159 org_req = environ.get('pylons.original_request')
160 160 if org_req:
161 161 path = org_req.environ.get('PATH_INFO')
162 162 return path
163 163
164 164
165 165 def get_user_agent(environ):
166 166 return environ.get('HTTP_USER_AGENT')
167 167
168 168
169 169 def vcs_operation_context(
170 170 environ, repo_name, username, action, scm, check_locking=True,
171 171 is_shadow_repo=False):
172 172 """
173 173 Generate the context for a vcs operation, e.g. push or pull.
174 174
175 175 This context is passed over the layers so that hooks triggered by the
176 176 vcs operation know details like the user, the user's IP address etc.
177 177
178 178 :param check_locking: Allows to switch of the computation of the locking
179 179 data. This serves mainly the need of the simplevcs middleware to be
180 180 able to disable this for certain operations.
181 181
182 182 """
183 183 # Tri-state value: False: unlock, None: nothing, True: lock
184 184 make_lock = None
185 185 locked_by = [None, None, None]
186 186 is_anonymous = username == User.DEFAULT_USER
187 187 if not is_anonymous and check_locking:
188 188 log.debug('Checking locking on repository "%s"', repo_name)
189 189 user = User.get_by_username(username)
190 190 repo = Repository.get_by_repo_name(repo_name)
191 191 make_lock, __, locked_by = repo.get_locking_state(
192 192 action, user.user_id)
193 193
194 194 settings_model = VcsSettingsModel(repo=repo_name)
195 195 ui_settings = settings_model.get_ui_settings()
196 196
197 197 extras = {
198 198 'ip': get_ip_addr(environ),
199 199 'username': username,
200 200 'action': action,
201 201 'repository': repo_name,
202 202 'scm': scm,
203 203 'config': rhodecode.CONFIG['__file__'],
204 204 'make_lock': make_lock,
205 205 'locked_by': locked_by,
206 206 'server_url': utils2.get_server_url(environ),
207 207 'user_agent': get_user_agent(environ),
208 208 'hooks': get_enabled_hook_classes(ui_settings),
209 209 'is_shadow_repo': is_shadow_repo,
210 210 }
211 211 return extras
212 212
213 213
214 214 class BasicAuth(AuthBasicAuthenticator):
215 215
216 216 def __init__(self, realm, authfunc, registry, auth_http_code=None,
217 217 initial_call_detection=False, acl_repo_name=None):
218 218 self.realm = realm
219 219 self.initial_call = initial_call_detection
220 220 self.authfunc = authfunc
221 221 self.registry = registry
222 222 self.acl_repo_name = acl_repo_name
223 223 self._rc_auth_http_code = auth_http_code
224 224
225 225 def _get_response_from_code(self, http_code):
226 226 try:
227 227 return get_exception(safe_int(http_code))
228 228 except Exception:
229 229 log.exception('Failed to fetch response for code %s' % http_code)
230 230 return HTTPForbidden
231 231
232 232 def build_authentication(self):
233 233 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
234 234 if self._rc_auth_http_code and not self.initial_call:
235 235 # return alternative HTTP code if alternative http return code
236 236 # is specified in RhodeCode config, but ONLY if it's not the
237 237 # FIRST call
238 238 custom_response_klass = self._get_response_from_code(
239 239 self._rc_auth_http_code)
240 240 return custom_response_klass(headers=head)
241 241 return HTTPUnauthorized(headers=head)
242 242
243 243 def authenticate(self, environ):
244 244 authorization = AUTHORIZATION(environ)
245 245 if not authorization:
246 246 return self.build_authentication()
247 247 (authmeth, auth) = authorization.split(' ', 1)
248 248 if 'basic' != authmeth.lower():
249 249 return self.build_authentication()
250 250 auth = auth.strip().decode('base64')
251 251 _parts = auth.split(':', 1)
252 252 if len(_parts) == 2:
253 253 username, password = _parts
254 254 if self.authfunc(
255 255 username, password, environ, VCS_TYPE,
256 256 registry=self.registry, acl_repo_name=self.acl_repo_name):
257 257 return username
258 258 if username and password:
259 259 # we mark that we actually executed authentication once, at
260 260 # that point we can use the alternative auth code
261 261 self.initial_call = False
262 262
263 263 return self.build_authentication()
264 264
265 265 __call__ = authenticate
266 266
267 267
268 268 def calculate_version_hash():
269 269 return md5(
270 270 config.get('beaker.session.secret', '') +
271 271 rhodecode.__version__)[:8]
272 272
273 273
274 274 def get_current_lang(request):
275 275 # NOTE(marcink): remove after pyramid move
276 276 try:
277 277 return translation.get_lang()[0]
278 278 except:
279 279 pass
280 280
281 return getattr(request, '_LOCALE_', None)
281 return getattr(request, '_LOCALE_', request.locale_name)
282 282
283 283
284 284 def attach_context_attributes(context, request, user_id):
285 285 """
286 286 Attach variables into template context called `c`, please note that
287 287 request could be pylons or pyramid request in here.
288 288 """
289 289
290 290 rc_config = SettingsModel().get_all_settings(cache=True)
291 291
292 292 context.rhodecode_version = rhodecode.__version__
293 293 context.rhodecode_edition = config.get('rhodecode.edition')
294 294 # unique secret + version does not leak the version but keep consistency
295 295 context.rhodecode_version_hash = calculate_version_hash()
296 296
297 297 # Default language set for the incoming request
298 298 context.language = get_current_lang(request)
299 299
300 300 # Visual options
301 301 context.visual = AttributeDict({})
302 302
303 303 # DB stored Visual Items
304 304 context.visual.show_public_icon = str2bool(
305 305 rc_config.get('rhodecode_show_public_icon'))
306 306 context.visual.show_private_icon = str2bool(
307 307 rc_config.get('rhodecode_show_private_icon'))
308 308 context.visual.stylify_metatags = str2bool(
309 309 rc_config.get('rhodecode_stylify_metatags'))
310 310 context.visual.dashboard_items = safe_int(
311 311 rc_config.get('rhodecode_dashboard_items', 100))
312 312 context.visual.admin_grid_items = safe_int(
313 313 rc_config.get('rhodecode_admin_grid_items', 100))
314 314 context.visual.repository_fields = str2bool(
315 315 rc_config.get('rhodecode_repository_fields'))
316 316 context.visual.show_version = str2bool(
317 317 rc_config.get('rhodecode_show_version'))
318 318 context.visual.use_gravatar = str2bool(
319 319 rc_config.get('rhodecode_use_gravatar'))
320 320 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
321 321 context.visual.default_renderer = rc_config.get(
322 322 'rhodecode_markup_renderer', 'rst')
323 323 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
324 324 context.visual.rhodecode_support_url = \
325 325 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
326 326
327 327 context.pre_code = rc_config.get('rhodecode_pre_code')
328 328 context.post_code = rc_config.get('rhodecode_post_code')
329 329 context.rhodecode_name = rc_config.get('rhodecode_title')
330 330 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
331 331 # if we have specified default_encoding in the request, it has more
332 332 # priority
333 333 if request.GET.get('default_encoding'):
334 334 context.default_encodings.insert(0, request.GET.get('default_encoding'))
335 335 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
336 336
337 337 # INI stored
338 338 context.labs_active = str2bool(
339 339 config.get('labs_settings_active', 'false'))
340 340 context.visual.allow_repo_location_change = str2bool(
341 341 config.get('allow_repo_location_change', True))
342 342 context.visual.allow_custom_hooks_settings = str2bool(
343 343 config.get('allow_custom_hooks_settings', True))
344 344 context.debug_style = str2bool(config.get('debug_style', False))
345 345
346 346 context.rhodecode_instanceid = config.get('instance_id')
347 347
348 348 context.visual.cut_off_limit_diff = safe_int(
349 349 config.get('cut_off_limit_diff'))
350 350 context.visual.cut_off_limit_file = safe_int(
351 351 config.get('cut_off_limit_file'))
352 352
353 353 # AppEnlight
354 354 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
355 355 context.appenlight_api_public_key = config.get(
356 356 'appenlight.api_public_key', '')
357 357 context.appenlight_server_url = config.get('appenlight.server_url', '')
358 358
359 359 # JS template context
360 360 context.template_context = {
361 361 'repo_name': None,
362 362 'repo_type': None,
363 363 'repo_landing_commit': None,
364 364 'rhodecode_user': {
365 365 'username': None,
366 366 'email': None,
367 367 'notification_status': False
368 368 },
369 369 'visual': {
370 370 'default_renderer': None
371 371 },
372 372 'commit_data': {
373 373 'commit_id': None
374 374 },
375 375 'pull_request_data': {'pull_request_id': None},
376 376 'timeago': {
377 377 'refresh_time': 120 * 1000,
378 378 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
379 379 },
380 380 'pylons_dispatch': {
381 381 # 'controller': request.environ['pylons.routes_dict']['controller'],
382 382 # 'action': request.environ['pylons.routes_dict']['action'],
383 383 },
384 384 'pyramid_dispatch': {
385 385
386 386 },
387 387 'extra': {'plugins': {}}
388 388 }
389 389 # END CONFIG VARS
390 390
391 391 # TODO: This dosn't work when called from pylons compatibility tween.
392 392 # Fix this and remove it from base controller.
393 393 # context.repo_name = get_repo_slug(request) # can be empty
394 394
395 395 diffmode = 'sideside'
396 396 if request.GET.get('diffmode'):
397 397 if request.GET['diffmode'] == 'unified':
398 398 diffmode = 'unified'
399 399 elif request.session.get('diffmode'):
400 400 diffmode = request.session['diffmode']
401 401
402 402 context.diffmode = diffmode
403 403
404 404 if request.session.get('diffmode') != diffmode:
405 405 request.session['diffmode'] = diffmode
406 406
407 407 context.csrf_token = auth.get_csrf_token(session=request.session)
408 408 context.backends = rhodecode.BACKENDS.keys()
409 409 context.backends.sort()
410 410 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
411 411
412 412 # NOTE(marcink): when migrated to pyramid we don't need to set this anymore,
413 413 # given request will ALWAYS be pyramid one
414 414 pyramid_request = pyramid.threadlocal.get_current_request()
415 415 context.pyramid_request = pyramid_request
416 416
417 417 # web case
418 418 if hasattr(pyramid_request, 'user'):
419 419 context.auth_user = pyramid_request.user
420 420 context.rhodecode_user = pyramid_request.user
421 421
422 422 # api case
423 423 if hasattr(pyramid_request, 'rpc_user'):
424 424 context.auth_user = pyramid_request.rpc_user
425 425 context.rhodecode_user = pyramid_request.rpc_user
426 426
427 427 # attach the whole call context to the request
428 428 request.call_context = context
429 429
430 430
431 431 def get_auth_user(request):
432 432 environ = request.environ
433 433 session = request.session
434 434
435 435 ip_addr = get_ip_addr(environ)
436 436 # make sure that we update permissions each time we call controller
437 437 _auth_token = (request.GET.get('auth_token', '') or
438 438 request.GET.get('api_key', ''))
439 439
440 440 if _auth_token:
441 441 # when using API_KEY we assume user exists, and
442 442 # doesn't need auth based on cookies.
443 443 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
444 444 authenticated = False
445 445 else:
446 446 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
447 447 try:
448 448 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
449 449 ip_addr=ip_addr)
450 450 except UserCreationError as e:
451 451 h.flash(e, 'error')
452 452 # container auth or other auth functions that create users
453 453 # on the fly can throw this exception signaling that there's
454 454 # issue with user creation, explanation should be provided
455 455 # in Exception itself. We then create a simple blank
456 456 # AuthUser
457 457 auth_user = AuthUser(ip_addr=ip_addr)
458 458
459 459 if password_changed(auth_user, session):
460 460 session.invalidate()
461 461 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
462 462 auth_user = AuthUser(ip_addr=ip_addr)
463 463
464 464 authenticated = cookie_store.get('is_authenticated')
465 465
466 466 if not auth_user.is_authenticated and auth_user.is_user_object:
467 467 # user is not authenticated and not empty
468 468 auth_user.set_authenticated(authenticated)
469 469
470 470 return auth_user
471 471
472 472
473 473 class BaseController(WSGIController):
474 474
475 475 def __before__(self):
476 476 """
477 477 __before__ is called before controller methods and after __call__
478 478 """
479 479 # on each call propagate settings calls into global settings.
480 480 set_rhodecode_config(config)
481 481 attach_context_attributes(c, request, self._rhodecode_user.user_id)
482 482
483 483 # TODO: Remove this when fixed in attach_context_attributes()
484 484 c.repo_name = get_repo_slug(request) # can be empty
485 485
486 486 self.cut_off_limit_diff = safe_int(config.get('cut_off_limit_diff'))
487 487 self.cut_off_limit_file = safe_int(config.get('cut_off_limit_file'))
488 488 self.sa = meta.Session
489 489 self.scm_model = ScmModel(self.sa)
490 490
491 491 # set user language
492 492 user_lang = getattr(c.pyramid_request, '_LOCALE_', None)
493 493 if user_lang:
494 494 translation.set_lang(user_lang)
495 495 log.debug('set language to %s for user %s',
496 496 user_lang, self._rhodecode_user)
497 497
498 498 def _dispatch_redirect(self, with_url, environ, start_response):
499 499 resp = HTTPFound(with_url)
500 500 environ['SCRIPT_NAME'] = '' # handle prefix middleware
501 501 environ['PATH_INFO'] = with_url
502 502 return resp(environ, start_response)
503 503
504 504 def __call__(self, environ, start_response):
505 505 """Invoke the Controller"""
506 506 # WSGIController.__call__ dispatches to the Controller method
507 507 # the request is routed to. This routing information is
508 508 # available in environ['pylons.routes_dict']
509 509 from rhodecode.lib import helpers as h
510 510
511 511 # Provide the Pylons context to Pyramid's debugtoolbar if it asks
512 512 if environ.get('debugtoolbar.wants_pylons_context', False):
513 513 environ['debugtoolbar.pylons_context'] = c._current_obj()
514 514
515 515 _route_name = '.'.join([environ['pylons.routes_dict']['controller'],
516 516 environ['pylons.routes_dict']['action']])
517 517
518 518 self.rc_config = SettingsModel().get_all_settings(cache=True)
519 519 self.ip_addr = get_ip_addr(environ)
520 520
521 521 # The rhodecode auth user is looked up and passed through the
522 522 # environ by the pylons compatibility tween in pyramid.
523 523 # So we can just grab it from there.
524 524 auth_user = environ['rc_auth_user']
525 525
526 526 # set globals for auth user
527 527 request.user = auth_user
528 528 self._rhodecode_user = auth_user
529 529
530 530 log.info('IP: %s User: %s accessed %s [%s]' % (
531 531 self.ip_addr, auth_user, safe_unicode(get_access_path(environ)),
532 532 _route_name)
533 533 )
534 534
535 535 user_obj = auth_user.get_instance()
536 536 if user_obj and user_obj.user_data.get('force_password_change'):
537 537 h.flash('You are required to change your password', 'warning',
538 538 ignore_duplicate=True)
539 539 return self._dispatch_redirect(
540 540 url('my_account_password'), environ, start_response)
541 541
542 542 return WSGIController.__call__(self, environ, start_response)
543 543
544 544
545 545 class BaseRepoController(BaseController):
546 546 """
547 547 Base class for controllers responsible for loading all needed data for
548 548 repository loaded items are
549 549
550 550 c.rhodecode_repo: instance of scm repository
551 551 c.rhodecode_db_repo: instance of db
552 552 c.repository_requirements_missing: shows that repository specific data
553 553 could not be displayed due to the missing requirements
554 554 c.repository_pull_requests: show number of open pull requests
555 555 """
556 556
557 557 def __before__(self):
558 558 super(BaseRepoController, self).__before__()
559 559 if c.repo_name: # extracted from routes
560 560 db_repo = Repository.get_by_repo_name(c.repo_name)
561 561 if not db_repo:
562 562 return
563 563
564 564 log.debug(
565 565 'Found repository in database %s with state `%s`',
566 566 safe_unicode(db_repo), safe_unicode(db_repo.repo_state))
567 567 route = getattr(request.environ.get('routes.route'), 'name', '')
568 568
569 569 # allow to delete repos that are somehow damages in filesystem
570 570 if route in ['delete_repo']:
571 571 return
572 572
573 573 if db_repo.repo_state in [Repository.STATE_PENDING]:
574 574 if route in ['repo_creating_home']:
575 575 return
576 576 check_url = url('repo_creating_home', repo_name=c.repo_name)
577 577 return redirect(check_url)
578 578
579 579 self.rhodecode_db_repo = db_repo
580 580
581 581 missing_requirements = False
582 582 try:
583 583 self.rhodecode_repo = self.rhodecode_db_repo.scm_instance()
584 584 except RepositoryRequirementError as e:
585 585 missing_requirements = True
586 586 self._handle_missing_requirements(e)
587 587
588 588 if self.rhodecode_repo is None and not missing_requirements:
589 589 log.error('%s this repository is present in database but it '
590 590 'cannot be created as an scm instance', c.repo_name)
591 591
592 592 h.flash(_(
593 593 "The repository at %(repo_name)s cannot be located.") %
594 594 {'repo_name': c.repo_name},
595 595 category='error', ignore_duplicate=True)
596 596 redirect(h.route_path('home'))
597 597
598 598 # update last change according to VCS data
599 599 if not missing_requirements:
600 600 commit = db_repo.get_commit(
601 601 pre_load=["author", "date", "message", "parents"])
602 602 db_repo.update_commit_cache(commit)
603 603
604 604 # Prepare context
605 605 c.rhodecode_db_repo = db_repo
606 606 c.rhodecode_repo = self.rhodecode_repo
607 607 c.repository_requirements_missing = missing_requirements
608 608
609 609 self._update_global_counters(self.scm_model, db_repo)
610 610
611 611 def _update_global_counters(self, scm_model, db_repo):
612 612 """
613 613 Base variables that are exposed to every page of repository
614 614 """
615 615 c.repository_pull_requests = scm_model.get_pull_requests(db_repo)
616 616
617 617 def _handle_missing_requirements(self, error):
618 618 self.rhodecode_repo = None
619 619 log.error(
620 620 'Requirements are missing for repository %s: %s',
621 621 c.repo_name, error.message)
622 622
623 623 summary_url = h.route_path('repo_summary', repo_name=c.repo_name)
624 624 statistics_url = url('edit_repo_statistics', repo_name=c.repo_name)
625 625 settings_update_url = url('repo', repo_name=c.repo_name)
626 626 path = request.path
627 627 should_redirect = (
628 628 path not in (summary_url, settings_update_url)
629 629 and '/settings' not in path or path == statistics_url
630 630 )
631 631 if should_redirect:
632 632 redirect(summary_url)
General Comments 0
You need to be logged in to leave comments. Login now