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