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