##// END OF EJS Templates
error-document: make sure the error document has registered helpers,...
marcink -
r1748:0fcdd9e3 default
parent child Browse files
Show More
@@ -1,45 +1,49 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-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 from rhodecode.config import routing_links
21 21
22 22
23 23 def includeme(config):
24 24
25 25 config.add_route(
26 name='home',
27 pattern='/')
28
29 config.add_route(
26 30 name='user_autocomplete_data',
27 31 pattern='/_users')
28 32
29 33 config.add_route(
30 34 name='user_group_autocomplete_data',
31 35 pattern='/_user_groups')
32 36
33 37 config.add_route(
34 38 name='repo_list_data',
35 39 pattern='/_repos')
36 40
37 41 config.add_route(
38 42 name='goto_switcher_data',
39 43 pattern='/_goto_data')
40 44
41 # register our static links via redirection mechanismy
45 # register our static links via redirection mechanism
42 46 routing_links.connect_redirection_links(config)
43 47
44 48 # Scan module for configuration decorators.
45 49 config.scan()
@@ -1,514 +1,515 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 Pylons middleware initialization
23 23 """
24 24 import logging
25 25 from collections import OrderedDict
26 26
27 27 from paste.registry import RegistryManager
28 28 from paste.gzipper import make_gzip_middleware
29 29 from pylons.wsgiapp import PylonsApp
30 30 from pyramid.authorization import ACLAuthorizationPolicy
31 31 from pyramid.config import Configurator
32 32 from pyramid.settings import asbool, aslist
33 33 from pyramid.wsgi import wsgiapp
34 34 from pyramid.httpexceptions import (
35 35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
36 36 from pyramid.events import ApplicationCreated
37 37 from pyramid.renderers import render_to_response
38 38 from routes.middleware import RoutesMiddleware
39 39 import routes.util
40 40
41 41 import rhodecode
42 42 from rhodecode.model import meta
43 43 from rhodecode.config import patches
44 44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 45 from rhodecode.config.environment import (
46 46 load_environment, load_pyramid_environment)
47 47 from rhodecode.lib.middleware import csrf
48 48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 49 from rhodecode.lib.middleware.error_handling import (
50 50 PylonsErrorHandlingMiddleware)
51 51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
52 52 from rhodecode.lib.middleware.vcs import VCSMiddleware
53 53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
54 54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
55 55 from rhodecode.subscribers import (
56 56 scan_repositories_if_enabled, write_js_routes_if_enabled,
57 57 write_metadata_if_needed)
58 58
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62
63 63 # this is used to avoid avoid the route lookup overhead in routesmiddleware
64 64 # for certain routes which won't go to pylons to - eg. static files, debugger
65 65 # it is only needed for the pylons migration and can be removed once complete
66 66 class SkippableRoutesMiddleware(RoutesMiddleware):
67 67 """ Routes middleware that allows you to skip prefixes """
68 68
69 69 def __init__(self, *args, **kw):
70 70 self.skip_prefixes = kw.pop('skip_prefixes', [])
71 71 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
72 72
73 73 def __call__(self, environ, start_response):
74 74 for prefix in self.skip_prefixes:
75 75 if environ['PATH_INFO'].startswith(prefix):
76 76 # added to avoid the case when a missing /_static route falls
77 77 # through to pylons and causes an exception as pylons is
78 78 # expecting wsgiorg.routingargs to be set in the environ
79 79 # by RoutesMiddleware.
80 80 if 'wsgiorg.routing_args' not in environ:
81 81 environ['wsgiorg.routing_args'] = (None, {})
82 82 return self.app(environ, start_response)
83 83
84 84 return super(SkippableRoutesMiddleware, self).__call__(
85 85 environ, start_response)
86 86
87 87
88 88 def make_app(global_conf, static_files=True, **app_conf):
89 89 """Create a Pylons WSGI application and return it
90 90
91 91 ``global_conf``
92 92 The inherited configuration for this application. Normally from
93 93 the [DEFAULT] section of the Paste ini file.
94 94
95 95 ``app_conf``
96 96 The application's local configuration. Normally specified in
97 97 the [app:<name>] section of the Paste ini file (where <name>
98 98 defaults to main).
99 99
100 100 """
101 101 # Apply compatibility patches
102 102 patches.kombu_1_5_1_python_2_7_11()
103 103 patches.inspect_getargspec()
104 104
105 105 # Configure the Pylons environment
106 106 config = load_environment(global_conf, app_conf)
107 107
108 108 # The Pylons WSGI app
109 109 app = PylonsApp(config=config)
110 110 if rhodecode.is_test:
111 111 app = csrf.CSRFDetector(app)
112 112
113 113 expected_origin = config.get('expected_origin')
114 114 if expected_origin:
115 115 # The API can be accessed from other Origins.
116 116 app = csrf.OriginChecker(app, expected_origin,
117 117 skip_urls=[routes.util.url_for('api')])
118 118
119 119 # Establish the Registry for this application
120 120 app = RegistryManager(app)
121 121
122 122 app.config = config
123 123
124 124 return app
125 125
126 126
127 127 def make_pyramid_app(global_config, **settings):
128 128 """
129 129 Constructs the WSGI application based on Pyramid and wraps the Pylons based
130 130 application.
131 131
132 132 Specials:
133 133
134 134 * We migrate from Pylons to Pyramid. While doing this, we keep both
135 135 frameworks functional. This involves moving some WSGI middlewares around
136 136 and providing access to some data internals, so that the old code is
137 137 still functional.
138 138
139 139 * The application can also be integrated like a plugin via the call to
140 140 `includeme`. This is accompanied with the other utility functions which
141 141 are called. Changing this should be done with great care to not break
142 142 cases when these fragments are assembled from another place.
143 143
144 144 """
145 145 # The edition string should be available in pylons too, so we add it here
146 146 # before copying the settings.
147 147 settings.setdefault('rhodecode.edition', 'Community Edition')
148 148
149 149 # As long as our Pylons application does expect "unprepared" settings, make
150 150 # sure that we keep an unmodified copy. This avoids unintentional change of
151 151 # behavior in the old application.
152 152 settings_pylons = settings.copy()
153 153
154 154 sanitize_settings_and_apply_defaults(settings)
155 155 config = Configurator(settings=settings)
156 156 add_pylons_compat_data(config.registry, global_config, settings_pylons)
157 157
158 158 load_pyramid_environment(global_config, settings)
159 159
160 160 includeme_first(config)
161 161 includeme(config)
162 162 pyramid_app = config.make_wsgi_app()
163 163 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
164 164 pyramid_app.config = config
165 165
166 166 # creating the app uses a connection - return it after we are done
167 167 meta.Session.remove()
168 168
169 169 return pyramid_app
170 170
171 171
172 172 def make_not_found_view(config):
173 173 """
174 174 This creates the view which should be registered as not-found-view to
175 175 pyramid. Basically it contains of the old pylons app, converted to a view.
176 176 Additionally it is wrapped by some other middlewares.
177 177 """
178 178 settings = config.registry.settings
179 179 vcs_server_enabled = settings['vcs.server.enable']
180 180
181 181 # Make pylons app from unprepared settings.
182 182 pylons_app = make_app(
183 183 config.registry._pylons_compat_global_config,
184 184 **config.registry._pylons_compat_settings)
185 185 config.registry._pylons_compat_config = pylons_app.config
186 186
187 187 # Appenlight monitoring.
188 188 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
189 189 pylons_app, settings)
190 190
191 191 # The pylons app is executed inside of the pyramid 404 exception handler.
192 192 # Exceptions which are raised inside of it are not handled by pyramid
193 193 # again. Therefore we add a middleware that invokes the error handler in
194 194 # case of an exception or error response. This way we return proper error
195 195 # HTML pages in case of an error.
196 196 reraise = (settings.get('debugtoolbar.enabled', False) or
197 197 rhodecode.disable_error_handler)
198 198 pylons_app = PylonsErrorHandlingMiddleware(
199 199 pylons_app, error_handler, reraise)
200 200
201 201 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
202 202 # view to handle the request. Therefore it is wrapped around the pylons
203 203 # app. It has to be outside of the error handling otherwise error responses
204 204 # from the vcsserver are converted to HTML error pages. This confuses the
205 205 # command line tools and the user won't get a meaningful error message.
206 206 if vcs_server_enabled:
207 207 pylons_app = VCSMiddleware(
208 208 pylons_app, settings, appenlight_client, registry=config.registry)
209 209
210 210 # Convert WSGI app to pyramid view and return it.
211 211 return wsgiapp(pylons_app)
212 212
213 213
214 214 def add_pylons_compat_data(registry, global_config, settings):
215 215 """
216 216 Attach data to the registry to support the Pylons integration.
217 217 """
218 218 registry._pylons_compat_global_config = global_config
219 219 registry._pylons_compat_settings = settings
220 220
221 221
222 222 def error_handler(exception, request):
223 223 import rhodecode
224 224 from rhodecode.lib.utils2 import AttributeDict
225 from rhodecode.lib import helpers
225 226
226 227 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
227 228
228 229 base_response = HTTPInternalServerError()
229 230 # prefer original exception for the response since it may have headers set
230 231 if isinstance(exception, HTTPException):
231 232 base_response = exception
232 233
233 234 def is_http_error(response):
234 235 # error which should have traceback
235 236 return response.status_code > 499
236 237
237 238 if is_http_error(base_response):
238 239 log.exception(
239 240 'error occurred handling this request for path: %s', request.path)
240 241
241 242 c = AttributeDict()
242 243 c.error_message = base_response.status
243 244 c.error_explanation = base_response.explanation or str(base_response)
244 245 c.visual = AttributeDict()
245 246
246 247 c.visual.rhodecode_support_url = (
247 248 request.registry.settings.get('rhodecode_support_url') or
248 249 request.route_url('rhodecode_support')
249 250 )
250 251 c.redirect_time = 0
251 252 c.rhodecode_name = rhodecode_title
252 253 if not c.rhodecode_name:
253 254 c.rhodecode_name = 'Rhodecode'
254 255
255 256 c.causes = []
256 257 if hasattr(base_response, 'causes'):
257 258 c.causes = base_response.causes
258
259 c.messages = helpers.flash.pop_messages()
259 260 response = render_to_response(
260 '/errors/error_document.mako', {'c': c}, request=request,
261 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
261 262 response=base_response)
262 263
263 264 return response
264 265
265 266
266 267 def includeme(config):
267 268 settings = config.registry.settings
268 269
269 270 # plugin information
270 271 config.registry.rhodecode_plugins = OrderedDict()
271 272
272 273 config.add_directive(
273 274 'register_rhodecode_plugin', register_rhodecode_plugin)
274 275
275 276 if asbool(settings.get('appenlight', 'false')):
276 277 config.include('appenlight_client.ext.pyramid_tween')
277 278
278 279 # Includes which are required. The application would fail without them.
279 280 config.include('pyramid_mako')
280 281 config.include('pyramid_beaker')
281 282
282 283 config.include('rhodecode.authentication')
283 284 config.include('rhodecode.integrations')
284 285
285 286 # apps
286 287 config.include('rhodecode.apps._base')
287 288 config.include('rhodecode.apps.ops')
288 289
289 290 config.include('rhodecode.apps.admin')
290 291 config.include('rhodecode.apps.channelstream')
291 292 config.include('rhodecode.apps.login')
292 293 config.include('rhodecode.apps.home')
293 294 config.include('rhodecode.apps.repository')
294 295 config.include('rhodecode.apps.search')
295 296 config.include('rhodecode.apps.user_profile')
296 297 config.include('rhodecode.apps.my_account')
297 298 config.include('rhodecode.apps.svn_support')
298 299
299 300 config.include('rhodecode.tweens')
300 301 config.include('rhodecode.api')
301 302
302 303 config.add_route(
303 304 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
304 305
305 306 config.add_translation_dirs('rhodecode:i18n/')
306 307 settings['default_locale_name'] = settings.get('lang', 'en')
307 308
308 309 # Add subscribers.
309 310 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
310 311 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
311 312 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
312 313
313 314 # Set the authorization policy.
314 315 authz_policy = ACLAuthorizationPolicy()
315 316 config.set_authorization_policy(authz_policy)
316 317
317 318 # Set the default renderer for HTML templates to mako.
318 319 config.add_mako_renderer('.html')
319 320
320 321 config.add_renderer(
321 322 name='json_ext',
322 323 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
323 324
324 325 # include RhodeCode plugins
325 326 includes = aslist(settings.get('rhodecode.includes', []))
326 327 for inc in includes:
327 328 config.include(inc)
328 329
329 330 # This is the glue which allows us to migrate in chunks. By registering the
330 331 # pylons based application as the "Not Found" view in Pyramid, we will
331 332 # fallback to the old application each time the new one does not yet know
332 333 # how to handle a request.
333 334 config.add_notfound_view(make_not_found_view(config))
334 335
335 336 if not settings.get('debugtoolbar.enabled', False):
336 337 # if no toolbar, then any exception gets caught and rendered
337 338 config.add_view(error_handler, context=Exception)
338 339
339 340 config.add_view(error_handler, context=HTTPError)
340 341
341 342
342 343 def includeme_first(config):
343 344 # redirect automatic browser favicon.ico requests to correct place
344 345 def favicon_redirect(context, request):
345 346 return HTTPFound(
346 347 request.static_path('rhodecode:public/images/favicon.ico'))
347 348
348 349 config.add_view(favicon_redirect, route_name='favicon')
349 350 config.add_route('favicon', '/favicon.ico')
350 351
351 352 def robots_redirect(context, request):
352 353 return HTTPFound(
353 354 request.static_path('rhodecode:public/robots.txt'))
354 355
355 356 config.add_view(robots_redirect, route_name='robots')
356 357 config.add_route('robots', '/robots.txt')
357 358
358 359 config.add_static_view(
359 360 '_static/deform', 'deform:static')
360 361 config.add_static_view(
361 362 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
362 363
363 364
364 365 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
365 366 """
366 367 Apply outer WSGI middlewares around the application.
367 368
368 369 Part of this has been moved up from the Pylons layer, so that the
369 370 data is also available if old Pylons code is hit through an already ported
370 371 view.
371 372 """
372 373 settings = config.registry.settings
373 374
374 375 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
375 376 pyramid_app = HttpsFixup(pyramid_app, settings)
376 377
377 378 # Add RoutesMiddleware to support the pylons compatibility tween during
378 379 # migration to pyramid.
379 380 pyramid_app = SkippableRoutesMiddleware(
380 381 pyramid_app, config.registry._pylons_compat_config['routes.map'],
381 382 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
382 383
383 384 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
384 385
385 386 if settings['gzip_responses']:
386 387 pyramid_app = make_gzip_middleware(
387 388 pyramid_app, settings, compress_level=1)
388 389
389 390 # this should be the outer most middleware in the wsgi stack since
390 391 # middleware like Routes make database calls
391 392 def pyramid_app_with_cleanup(environ, start_response):
392 393 try:
393 394 return pyramid_app(environ, start_response)
394 395 finally:
395 396 # Dispose current database session and rollback uncommitted
396 397 # transactions.
397 398 meta.Session.remove()
398 399
399 400 # In a single threaded mode server, on non sqlite db we should have
400 401 # '0 Current Checked out connections' at the end of a request,
401 402 # if not, then something, somewhere is leaving a connection open
402 403 pool = meta.Base.metadata.bind.engine.pool
403 404 log.debug('sa pool status: %s', pool.status())
404 405
405 406
406 407 return pyramid_app_with_cleanup
407 408
408 409
409 410 def sanitize_settings_and_apply_defaults(settings):
410 411 """
411 412 Applies settings defaults and does all type conversion.
412 413
413 414 We would move all settings parsing and preparation into this place, so that
414 415 we have only one place left which deals with this part. The remaining parts
415 416 of the application would start to rely fully on well prepared settings.
416 417
417 418 This piece would later be split up per topic to avoid a big fat monster
418 419 function.
419 420 """
420 421
421 422 # Pyramid's mako renderer has to search in the templates folder so that the
422 423 # old templates still work. Ported and new templates are expected to use
423 424 # real asset specifications for the includes.
424 425 mako_directories = settings.setdefault('mako.directories', [
425 426 # Base templates of the original Pylons application
426 427 'rhodecode:templates',
427 428 ])
428 429 log.debug(
429 430 "Using the following Mako template directories: %s",
430 431 mako_directories)
431 432
432 433 # Default includes, possible to change as a user
433 434 pyramid_includes = settings.setdefault('pyramid.includes', [
434 435 'rhodecode.lib.middleware.request_wrapper',
435 436 ])
436 437 log.debug(
437 438 "Using the following pyramid.includes: %s",
438 439 pyramid_includes)
439 440
440 441 # TODO: johbo: Re-think this, usually the call to config.include
441 442 # should allow to pass in a prefix.
442 443 settings.setdefault('rhodecode.api.url', '/_admin/api')
443 444
444 445 # Sanitize generic settings.
445 446 _list_setting(settings, 'default_encoding', 'UTF-8')
446 447 _bool_setting(settings, 'is_test', 'false')
447 448 _bool_setting(settings, 'gzip_responses', 'false')
448 449
449 450 # Call split out functions that sanitize settings for each topic.
450 451 _sanitize_appenlight_settings(settings)
451 452 _sanitize_vcs_settings(settings)
452 453
453 454 return settings
454 455
455 456
456 457 def _sanitize_appenlight_settings(settings):
457 458 _bool_setting(settings, 'appenlight', 'false')
458 459
459 460
460 461 def _sanitize_vcs_settings(settings):
461 462 """
462 463 Applies settings defaults and does type conversion for all VCS related
463 464 settings.
464 465 """
465 466 _string_setting(settings, 'vcs.svn.compatible_version', '')
466 467 _string_setting(settings, 'git_rev_filter', '--all')
467 468 _string_setting(settings, 'vcs.hooks.protocol', 'http')
468 469 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
469 470 _string_setting(settings, 'vcs.server', '')
470 471 _string_setting(settings, 'vcs.server.log_level', 'debug')
471 472 _string_setting(settings, 'vcs.server.protocol', 'http')
472 473 _bool_setting(settings, 'startup.import_repos', 'false')
473 474 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
474 475 _bool_setting(settings, 'vcs.server.enable', 'true')
475 476 _bool_setting(settings, 'vcs.start_server', 'false')
476 477 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
477 478 _int_setting(settings, 'vcs.connection_timeout', 3600)
478 479
479 480 # Support legacy values of vcs.scm_app_implementation. Legacy
480 481 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
481 482 # which is now mapped to 'http'.
482 483 scm_app_impl = settings['vcs.scm_app_implementation']
483 484 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
484 485 settings['vcs.scm_app_implementation'] = 'http'
485 486
486 487
487 488 def _int_setting(settings, name, default):
488 489 settings[name] = int(settings.get(name, default))
489 490
490 491
491 492 def _bool_setting(settings, name, default):
492 493 input = settings.get(name, default)
493 494 if isinstance(input, unicode):
494 495 input = input.encode('utf8')
495 496 settings[name] = asbool(input)
496 497
497 498
498 499 def _list_setting(settings, name, default):
499 500 raw_value = settings.get(name, default)
500 501
501 502 old_separator = ','
502 503 if old_separator in raw_value:
503 504 # If we get a comma separated list, pass it to our own function.
504 505 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
505 506 else:
506 507 # Otherwise we assume it uses pyramids space/newline separation.
507 508 settings[name] = aslist(raw_value)
508 509
509 510
510 511 def _string_setting(settings, name, default, lower=True):
511 512 value = settings.get(name, default)
512 513 if lower:
513 514 value = value.lower()
514 515 settings[name] = value
@@ -1,77 +1,76 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html>
3 3 <html xmlns="http://www.w3.org/1999/xhtml">
4 4 <head>
5 5 <title>Error - ${c.error_message}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <meta name="robots" content="index, nofollow"/>
8 8 <link rel="icon" href="${h.asset('images/favicon.ico')}" sizes="16x16 32x32" type="image/png" />
9 9
10 10 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
11 11 %if c.redirect_time:
12 12 <meta http-equiv="refresh" content="${c.redirect_time}; url=${c.url_redirect}"/>
13 13 %endif
14 14
15 15 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
16 16 <!--[if IE]>
17 17 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css')}" media="screen"/>
18 18 <![endif]-->
19 19 <style>body { background:#eeeeee; }</style>
20 20 <script type="text/javascript">
21 21 // register templateContext to pass template variables to JS
22 22 var templateContext = {timeago: {}};
23 23 </script>
24 24 <script type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
25 25 </head>
26 26 <body>
27 <% messages = h.flash.pop_messages() %>
28 27
29 28 <div class="wrapper error_page">
30 29 <div class="sidebar">
31 <a href="${h.url('home')}"><img class="error-page-logo" src="${h.asset('images/RhodeCode_Logo_Black.png')}" alt="RhodeCode"/></a>
30 <a href="${h.route_path('home')}"><img class="error-page-logo" src="${h.asset('images/RhodeCode_Logo_Black.png')}" alt="RhodeCode"/></a>
32 31 </div>
33 32 <div class="main-content">
34 33 <h1>
35 34 <span class="error-branding">
36 35 ${h.branding(c.rhodecode_name)}
37 36 </span><br/>
38 37 ${c.error_message} | <span class="error_message">${c.error_explanation}</span>
39 38 </h1>
40 % if messages:
41 % for message in messages:
39 % if c.messages:
40 % for message in c.messages:
42 41 <div class="alert alert-${message.category}">${message}</div>
43 42 % endfor
44 43 % endif
45 44 %if c.redirect_time:
46 45 <p>${_('You will be redirected to %s in %s seconds') % (c.redirect_module,c.redirect_time)}</p>
47 46 %endif
48 47 <div class="inner-column">
49 48 <h4>Possible Causes</h4>
50 49 <ul>
51 50 % if c.causes:
52 51 %for cause in c.causes:
53 52 <li>${cause}</li>
54 53 %endfor
55 54 %else:
56 55 <li>The resource may have been deleted.</li>
57 56 <li>You may not have access to this repository.</li>
58 57 <li>The link may be incorrect.</li>
59 58 %endif
60 59 </ul>
61 60 </div>
62 61 <div class="inner-column">
63 62 <h4>Support</h4>
64 63 <p>For support, go to <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>.
65 64 It may be useful to include your log file; see the log file locations <a href="${h.route_url('enterprise_log_file_locations')}">here</a>.
66 65 </p>
67 66 </div>
68 67 <div class="inner-column">
69 68 <h4>Documentation</h4>
70 69 <p>For more information, see <a href="${h.route_url('enterprise_docs')}">docs.rhodecode.com</a>.</p>
71 70 </div>
72 71 </div>
73 72 </div>
74 73
75 74 </body>
76 75
77 76 </html>
General Comments 0
You need to be logged in to leave comments. Login now