[IMP] http_routing, *: move error pages to http_routing
* = portal, website, test_website, account, sms Portal an Survey error pages were ugly default pages. Now the error pages will be the same as the website ones. Also fixing single module builds tests task-2059969 closes odoo/odoo#35535 Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
This commit is contained in:
@@ -10,7 +10,7 @@ from odoo.tools import mute_logger
|
||||
@tests.tagged('post_install', '-at_install')
|
||||
class TestUi(tests.HttpCase):
|
||||
|
||||
@mute_logger('odoo.addons.website.models.ir_http', 'odoo.http')
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_01_portal_attachment(self):
|
||||
"""Test the portal chatter attachment route."""
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ Proposes advanced routing options not available in web or base to keep
|
||||
base modules simple.
|
||||
""",
|
||||
'data': [
|
||||
'views/http_routing_template.xml',
|
||||
'views/res_lang_views.xml',
|
||||
],
|
||||
'depends': ['web'],
|
||||
|
||||
@@ -5,6 +5,7 @@ import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
import unicodedata
|
||||
import werkzeug
|
||||
|
||||
@@ -15,8 +16,9 @@ except ImportError:
|
||||
slugify_lib = None
|
||||
|
||||
import odoo
|
||||
from odoo import api, models
|
||||
from odoo import api, models, registry, exceptions
|
||||
from odoo.addons.base.models.ir_http import RequestUID, ModelConverter
|
||||
from odoo.addons.base.models.qweb import QWebException
|
||||
from odoo.http import request
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import config, ustr, pycompat
|
||||
@@ -540,3 +542,87 @@ class IrHttp(models.AbstractModel):
|
||||
if request.httprequest.query_string:
|
||||
path += '?' + request.httprequest.query_string.decode('utf-8')
|
||||
return werkzeug.utils.redirect(path, code=301)
|
||||
|
||||
@classmethod
|
||||
def _get_exception_code_values(cls, exception):
|
||||
""" Return a tuple with the error code following by the values matching the exception"""
|
||||
code = 500 # default code
|
||||
values = dict(
|
||||
exception=exception,
|
||||
traceback=traceback.format_exc(),
|
||||
)
|
||||
# only except_orm exceptions contain a message
|
||||
if isinstance(exception, exceptions.except_orm):
|
||||
values['error_message'] = exception.name
|
||||
code = 400
|
||||
if isinstance(exception, exceptions.AccessError):
|
||||
code = 403
|
||||
|
||||
elif isinstance(exception, QWebException):
|
||||
values.update(qweb_exception=exception)
|
||||
|
||||
if type(exception.error) == exceptions.AccessError:
|
||||
code = 403
|
||||
|
||||
elif isinstance(exception, werkzeug.exceptions.HTTPException):
|
||||
code = exception.code
|
||||
|
||||
values.update(
|
||||
status_message=werkzeug.http.HTTP_STATUS_CODES[code],
|
||||
status_code=code,
|
||||
)
|
||||
|
||||
return (code, values)
|
||||
|
||||
@classmethod
|
||||
def _get_values_500_error(cls, env, values, exception):
|
||||
values['view'] = env["ir.ui.view"]
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def _get_error_html(cls, env, code, values):
|
||||
return env['ir.ui.view'].render_template('http_routing.%s' % code, values)
|
||||
|
||||
@classmethod
|
||||
def _handle_exception(cls, exception):
|
||||
is_frontend_request = bool(getattr(request, 'is_frontend', False))
|
||||
if not is_frontend_request:
|
||||
# Don't touch non frontend requests exception handling
|
||||
return super(IrHttp, cls)._handle_exception(exception)
|
||||
try:
|
||||
response = super(IrHttp, cls)._handle_exception(exception)
|
||||
|
||||
if isinstance(response, Exception):
|
||||
exception = response
|
||||
else:
|
||||
# if parent excplicitely returns a plain response, then we don't touch it
|
||||
return response
|
||||
except Exception as e:
|
||||
if 'werkzeug' in config['dev_mode']:
|
||||
raise e
|
||||
exception = e
|
||||
|
||||
code, values = cls._get_exception_code_values(exception)
|
||||
|
||||
if code is None:
|
||||
# Hand-crafted HTTPException likely coming from abort(),
|
||||
# usually for a redirect response -> return it directly
|
||||
return exception
|
||||
|
||||
if not request.uid:
|
||||
cls._auth_method_public()
|
||||
with registry(request.env.cr.dbname).cursor() as cr:
|
||||
env = api.Environment(cr, request.uid, request.env.context)
|
||||
if code == 500:
|
||||
_logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
|
||||
values = cls._get_values_500_error(env, values, exception)
|
||||
elif code == 403:
|
||||
_logger.warn("403 Forbidden:\n\n%s", values['traceback'])
|
||||
elif code == 400:
|
||||
_logger.warn("400 Bad Request:\n\n%s", values['traceback'])
|
||||
try:
|
||||
html = cls._get_error_html(env, code, values)
|
||||
except Exception:
|
||||
html = env['ir.ui.view'].render_template('http_routing.http_error', values)
|
||||
|
||||
return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="http_error">
|
||||
<t t-call="web.frontend_layout">
|
||||
<div id="wrap">
|
||||
<div class="oe_structure">
|
||||
<h1 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h1>
|
||||
</div>
|
||||
<t t-if="editable or debug">
|
||||
<t t-call="http_routing.http_error_debug"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="error_message">
|
||||
<p t-if="error_message">
|
||||
<strong>Error message:</strong>
|
||||
<pre t-esc="error_message"/>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template id="http_error_debug">
|
||||
<div class="container accordion mb32 mt32" id="debug_infos">
|
||||
<div class="card" t-if="error_message">
|
||||
<h4 class="card-header">
|
||||
<a data-toggle="collapse" href="#error_main">Error</a>
|
||||
</h4>
|
||||
<div id="error_main" class="collapse show">
|
||||
<div class="card-body">
|
||||
<t t-call="http_routing.error_message"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card" t-if="qweb_exception">
|
||||
<h4 class="card-header">
|
||||
<a data-toggle="collapse" href="#error_qweb">QWeb</a>
|
||||
</h4>
|
||||
<div id="error_qweb" class="collapse show">
|
||||
<div class="card-body">
|
||||
<p t-if="exception.message">
|
||||
<strong>Error message:</strong>
|
||||
<pre t-esc="exception.message"/>
|
||||
</p>
|
||||
<p>
|
||||
The error occured while rendering the template <code t-esc="qweb_exception.name"/>
|
||||
<t t-if="qweb_exception.html">and evaluating the following expression: <code t-esc="qweb_exception.html"/></t>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card" t-if="traceback">
|
||||
<h4 class="card-header">
|
||||
<a data-toggle="collapse" href="#error_traceback">Traceback</a>
|
||||
</h4>
|
||||
<div id="error_traceback" t-attf-class="collapse #{not error_message and not qweb_exception and 'show'}">
|
||||
<div class="card-body">
|
||||
<pre id="exception_traceback" t-esc="traceback"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="400">
|
||||
<t t-call="web.frontend_layout">
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<h1 class="mt-5">Oops! Something went wrong.</h1>
|
||||
<p>Take a look at the error message below.</p>
|
||||
</div>
|
||||
<t t-if="editable or request.session.debug">
|
||||
<t t-call="http_routing.http_error_debug"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="container">
|
||||
<t t-call="http_routing.error_message"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="403">
|
||||
<t t-call="web.frontend_layout">
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<h1 class="mt-5">403: Forbidden</h1>
|
||||
<p>The page you were looking for could not be authorized.</p>
|
||||
</div>
|
||||
<t t-if="editable or request.session.debug">
|
||||
<t t-call="http_routing.http_error_debug"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="container">
|
||||
<t t-call="http_routing.error_message"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="404">
|
||||
<t t-call="web.frontend_layout">
|
||||
<div id="wrap">
|
||||
<t t-raw="0"/>
|
||||
<div class="oe_structure oe_empty">
|
||||
<div class="container">
|
||||
<h1 class="mt-5">404: Page not found!</h1>
|
||||
<p>
|
||||
The page you were looking for could not be found; it is possible you have
|
||||
typed the address incorrectly, but it has most probably been removed due
|
||||
to the recent reorganisation.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="500">
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<!-- This template should not use any variable except those provided by http_routing.ir_http._handle_exception -->
|
||||
<!-- no request.crsf_token, no theme style, no assets, ... cursor can be broken during rendering ! -->
|
||||
<!-- see test_05_reset_specific_view_controller_broken_request -->
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<html>
|
||||
<head>
|
||||
<title t-esc="status_message">Internal Server Error</title>
|
||||
<t t-set="debug" t-value="True"/>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css"/>
|
||||
<script src="/web/static/lib/jquery/jquery.js" type="text/javascript"/>
|
||||
<script type="text/javascript" src="/web/static/lib/bootstrap/js/util.js"/>
|
||||
<script type="text/javascript" src="/web/static/lib/bootstrap/js/collapse.js"/>
|
||||
<style>
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapwrap">
|
||||
<header>
|
||||
<div class="navbar navbar-expand-md navbar-light bg-light">
|
||||
<div class="container">
|
||||
<div class="collapse navbar-collapse navbar-top-collapse">
|
||||
<ul class="navbar-nav ml-auto" id="top_menu">
|
||||
<li class="nav-item"><a href="/" class="nav-link">Home</a></li>
|
||||
<li class="nav-item"><a href="javascript: window.history.back()" class="nav-link">Back</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<div id="error_message" class="oe_structure">
|
||||
<h2 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h2>
|
||||
</div>
|
||||
|
||||
<t t-if="editable or debug">
|
||||
<t t-call="http_routing.http_error_debug"/>
|
||||
</t>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -493,4 +493,13 @@
|
||||
</body>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="portal_404" inherit_id="http_routing.404">
|
||||
<xpath expr="//p" position="after">
|
||||
<p>Maybe you were looking for one of these popular pages ?</p>
|
||||
<ul>
|
||||
<li><a href="/my">Documents</a></li>
|
||||
</ul>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<template id="qunit_suite" name="sms_widget_tests" inherit_id="web.qunit_suite">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/sms/static/src/tests/sms_widget_test.js"></script>
|
||||
<script type="text/javascript" src="/sms/static/tests/sms_widget_test.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
|
||||
@@ -5,6 +5,6 @@ from odoo.tools import mute_logger
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestWebsiteError(odoo.tests.HttpCase):
|
||||
|
||||
@mute_logger('odoo.addons.website.models.ir_http', 'odoo.http')
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_01_run_test(self):
|
||||
self.start_tour("/test_error_view", 'test_error_website')
|
||||
|
||||
@@ -31,7 +31,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
|
||||
self.View = self.env['ir.ui.view']
|
||||
self.test_view = self.Website.viewref('test_website.test_view')
|
||||
|
||||
@mute_logger('odoo.addons.website.models.ir_http')
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http')
|
||||
def test_01_reset_specific_page_view(self):
|
||||
self.test_page_view = self.Website.viewref('test_website.test_page_view')
|
||||
total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
@@ -40,7 +40,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
|
||||
self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
|
||||
self.fix_it('/test_page_view')
|
||||
|
||||
@mute_logger('odoo.addons.website.models.ir_http')
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http')
|
||||
def test_02_reset_specific_view_controller(self):
|
||||
total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
# Trigger COW then break the QWEB XML on it
|
||||
@@ -49,7 +49,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
|
||||
self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
|
||||
self.fix_it('/test_view')
|
||||
|
||||
@mute_logger('odoo.addons.website.models.ir_http')
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http')
|
||||
def test_03_reset_specific_view_controller_t_called(self):
|
||||
self.test_view_to_be_t_called = self.Website.viewref('test_website.test_view_to_be_t_called')
|
||||
|
||||
@@ -60,7 +60,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
|
||||
self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
|
||||
self.fix_it('/test_view')
|
||||
|
||||
@mute_logger('odoo.addons.website.models.ir_http')
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http')
|
||||
def test_04_reset_specific_view_controller_inherit(self):
|
||||
self.test_view_child_broken = self.Website.viewref('test_website.test_view_child_broken')
|
||||
|
||||
@@ -71,7 +71,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
|
||||
self.fix_it('/test_view')
|
||||
|
||||
# This test work in real life, but not in test mode since we cannot rollback savepoint.
|
||||
# @mute_logger('odoo.addons.website.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
|
||||
# @mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
|
||||
# def test_05_reset_specific_view_controller_broken_request(self):
|
||||
# total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
# # Trigger COW then break the QWEB XML on it
|
||||
@@ -80,7 +80,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
|
||||
# self.fix_it('/test_view')
|
||||
|
||||
# also mute ir.ui.view as `get_view_id()` will raise "Could not find view object with xml_id 'not.exist'""
|
||||
@mute_logger('odoo.addons.website.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
|
||||
def test_06_reset_specific_view_controller_inexisting_template(self):
|
||||
total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
# Trigger COW then break the QWEB XML on it
|
||||
@@ -88,14 +88,14 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
|
||||
self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view (2)")
|
||||
self.fix_it('/test_view')
|
||||
|
||||
@mute_logger('odoo.addons.website.models.ir_http')
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http')
|
||||
def test_07_reset_page_view_complete_flow(self):
|
||||
self.start_tour("/", 'test_reset_page_view_complete_flow_part1', login="admin")
|
||||
self.fix_it('/test_page_view')
|
||||
self.start_tour("/", 'test_reset_page_view_complete_flow_part2', login="admin")
|
||||
self.fix_it('/test_page_view')
|
||||
|
||||
@mute_logger('odoo.addons.website.models.ir_http')
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http')
|
||||
def test_08_reset_specific_page_view_hard_mode(self):
|
||||
self.test_page_view = self.Website.viewref('test_website.test_page_view')
|
||||
total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import logging
|
||||
from lxml import etree
|
||||
import traceback
|
||||
import os
|
||||
import unittest
|
||||
|
||||
@@ -14,14 +13,12 @@ import werkzeug.utils
|
||||
from functools import partial
|
||||
|
||||
import odoo
|
||||
from odoo import api, models, registry
|
||||
from odoo import api, models
|
||||
from odoo import SUPERUSER_ID
|
||||
from odoo.http import request
|
||||
from odoo.tools import config
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from odoo.osv.expression import FALSE_DOMAIN, OR
|
||||
|
||||
from odoo.addons.base.models.qweb import QWebException
|
||||
from odoo.addons.http_routing.models.ir_http import ModelConverter, _guess_mimetype
|
||||
from odoo.addons.portal.controllers.portal import _build_url_w_params
|
||||
|
||||
@@ -239,7 +236,6 @@ class Http(models.AbstractModel):
|
||||
'deletable': True,
|
||||
'main_object': mypage,
|
||||
}, mimetype=_guess_mimetype(ext))
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def _serve_redirect(cls):
|
||||
@@ -269,109 +265,47 @@ class Http(models.AbstractModel):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def _handle_exception(cls, exception):
|
||||
code = 500 # default code
|
||||
is_website_request = bool(getattr(request, 'is_frontend', False) and get_request_website())
|
||||
if not is_website_request:
|
||||
# Don't touch non website requests exception handling
|
||||
return super(Http, cls)._handle_exception(exception)
|
||||
else:
|
||||
def _get_exception_code_values(cls, exception):
|
||||
code, values = super(Http, cls)._get_exception_code_values(exception)
|
||||
if request.website.is_publisher() and isinstance(exception, werkzeug.exceptions.NotFound):
|
||||
code = 'page_404'
|
||||
values['path'] = request.httprequest.path[1:]
|
||||
return (code, values)
|
||||
|
||||
@classmethod
|
||||
def _get_values_500_error(cls, env, values, exception):
|
||||
View = env["ir.ui.view"]
|
||||
values = super(Http, cls)._get_values_500_error(env, values, exception)
|
||||
if 'qweb_exception' in values:
|
||||
try:
|
||||
response = super(Http, cls)._handle_exception(exception)
|
||||
# exception.name might be int, string
|
||||
exception_template = int(exception.name)
|
||||
except:
|
||||
exception_template = exception.name
|
||||
view = View._view_obj(exception_template)
|
||||
if exception.html and exception.html in view.arch:
|
||||
values['view'] = view
|
||||
else:
|
||||
# There might be 2 cases where the exception code can't be found
|
||||
# in the view, either the error is in a child view or the code
|
||||
# contains branding (<div t-att-data="request.browse('ok')"/>).
|
||||
et = etree.fromstring(view.with_context(inherit_branding=False).read_combined(['arch'])['arch'])
|
||||
node = et.xpath(exception.path)
|
||||
line = node is not None and etree.tostring(node[0], encoding='unicode')
|
||||
if line:
|
||||
values['view'] = View._views_get(exception_template).filtered(
|
||||
lambda v: line in v.arch
|
||||
)
|
||||
values['view'] = values['view'] and values['view'][0]
|
||||
# Needed to show reset template on translated pages (`_prepare_qcontext` will set it for main lang)
|
||||
values['editable'] = request.uid and request.website.is_publisher()
|
||||
return values
|
||||
|
||||
if isinstance(response, Exception):
|
||||
exception = response
|
||||
else:
|
||||
# if parent excplicitely returns a plain response, then we don't touch it
|
||||
return response
|
||||
except Exception as e:
|
||||
if 'werkzeug' in config['dev_mode']:
|
||||
raise e
|
||||
exception = e
|
||||
|
||||
values = dict(
|
||||
exception=exception,
|
||||
traceback=traceback.format_exc(),
|
||||
)
|
||||
|
||||
# only except_orm exceptions contain a message
|
||||
if isinstance(exception, odoo.exceptions.except_orm):
|
||||
values['error_message'] = exception.name
|
||||
code = 400
|
||||
|
||||
if isinstance(exception, werkzeug.exceptions.HTTPException):
|
||||
if exception.code is None:
|
||||
# Hand-crafted HTTPException likely coming from abort(),
|
||||
# usually for a redirect response -> return it directly
|
||||
return exception
|
||||
else:
|
||||
code = exception.code
|
||||
|
||||
if isinstance(exception, odoo.exceptions.AccessError):
|
||||
code = 403
|
||||
|
||||
if isinstance(exception, QWebException):
|
||||
values.update(qweb_exception=exception)
|
||||
|
||||
# retro compatibility to remove in 12.2
|
||||
exception.qweb = dict(message=exception.message, expression=exception.html)
|
||||
|
||||
if type(exception.error) == odoo.exceptions.AccessError:
|
||||
code = 403
|
||||
|
||||
values.update(
|
||||
status_message=werkzeug.http.HTTP_STATUS_CODES[code],
|
||||
status_code=code,
|
||||
)
|
||||
|
||||
view_id = code
|
||||
if request.website.is_publisher() and isinstance(exception, werkzeug.exceptions.NotFound):
|
||||
view_id = 'page_404'
|
||||
values['path'] = request.httprequest.path[1:]
|
||||
|
||||
if not request.uid:
|
||||
cls._auth_method_public()
|
||||
|
||||
with registry(request.env.cr.dbname).cursor() as cr:
|
||||
env = api.Environment(cr, request.uid, request.env.context)
|
||||
if code == 500:
|
||||
logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
|
||||
View = env["ir.ui.view"]
|
||||
values['view'] = View
|
||||
if 'qweb_exception' in values:
|
||||
try:
|
||||
# exception.name might be int, string
|
||||
exception_template = int(exception.name)
|
||||
except:
|
||||
exception_template = exception.name
|
||||
view = View._view_obj(exception_template)
|
||||
if exception.html and exception.html in view.arch:
|
||||
values['view'] = view
|
||||
else:
|
||||
# There might be 2 cases where the exception code can't be found
|
||||
# in the view, either the error is in a child view or the code
|
||||
# contains branding (<div t-att-data="request.browse('ok')"/>).
|
||||
et = etree.fromstring(view.with_context(inherit_branding=False).read_combined(['arch'])['arch'])
|
||||
node = et.xpath(exception.path)
|
||||
line = node is not None and etree.tostring(node[0], encoding='unicode')
|
||||
if line:
|
||||
values['view'] = View._views_get(exception_template).filtered(
|
||||
lambda v: line in v.arch
|
||||
)
|
||||
values['view'] = values['view'] and values['view'][0]
|
||||
|
||||
# Needed to show reset template on translated pages (`_prepare_qcontext` will set it for main lang)
|
||||
values['editable'] = request.uid and request.website.is_publisher()
|
||||
elif code == 403:
|
||||
logger.warn("403 Forbidden:\n\n%s", values['traceback'])
|
||||
elif code == 400:
|
||||
logger.warn("400 Bad Request:\n\n%s", values['traceback'])
|
||||
try:
|
||||
html = env['ir.ui.view'].render_template('website.%s' % view_id, values)
|
||||
except Exception:
|
||||
html = env['ir.ui.view'].render_template('website.http_error', values)
|
||||
|
||||
return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
|
||||
@classmethod
|
||||
def _get_error_html(cls, env, code, values):
|
||||
if code == 'page_404':
|
||||
return env['ir.ui.view'].render_template('website.%s' % code, values)
|
||||
return super(Http, cls)._get_error_html(env, code, values)
|
||||
|
||||
def binary_content(self, xmlid=None, model='ir.attachment', id=None, field='datas',
|
||||
unique=False, filename=None, filename_field='name', download=False,
|
||||
|
||||
@@ -788,7 +788,7 @@
|
||||
</template>
|
||||
|
||||
<template id="page_404">
|
||||
<t t-call="website.404">
|
||||
<t t-call="http_routing.404">
|
||||
<div class="container">
|
||||
<div class="alert alert-info mt32">
|
||||
<p>This page does not exist, but you can create it as you are administrator of this site.</p>
|
||||
@@ -800,256 +800,104 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="http_error">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="oe_structure">
|
||||
<h1 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h1>
|
||||
</div>
|
||||
<t t-if="editable or debug">
|
||||
<t t-call="website.http_error_debug"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="error_message">
|
||||
<p t-if="error_message">
|
||||
<strong>Error message:</strong>
|
||||
<pre t-esc="error_message"/>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template id="http_error_debug">
|
||||
<div class="container accordion mb32 mt32" id="debug_infos">
|
||||
<div class="card" t-if="error_message">
|
||||
<h4 class="card-header">
|
||||
<a data-toggle="collapse" href="#error_main">Error</a>
|
||||
</h4>
|
||||
<div id="error_main" class="collapse show">
|
||||
<div class="card-body">
|
||||
<t t-call="website.error_message"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card" t-if="qweb_exception">
|
||||
<h4 class="card-header">
|
||||
<a data-toggle="collapse" href="#error_qweb">QWeb</a>
|
||||
</h4>
|
||||
<div id="error_qweb" class="collapse show">
|
||||
<div class="card-body">
|
||||
<p t-if="exception.message">
|
||||
<strong>Error message:</strong>
|
||||
<pre t-esc="exception.message"/>
|
||||
</p>
|
||||
<p>
|
||||
The error occured while rendering the template <code t-esc="qweb_exception.name"/>
|
||||
<t t-if="qweb_exception.html">and evaluating the following expression: <code t-esc="qweb_exception.html"/></t>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card" t-if="traceback">
|
||||
<h4 class="card-header">
|
||||
<a data-toggle="collapse" href="#error_traceback">Traceback</a>
|
||||
</h4>
|
||||
<div id="error_traceback" t-attf-class="collapse #{not error_message and not qweb_exception and 'show'}">
|
||||
<div class="card-body">
|
||||
<pre id="exception_traceback" t-esc="traceback"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="400">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<h1 class="mt-5">Something went wrong.</h1>
|
||||
<p>Take a look at the error message below.</p>
|
||||
</div>
|
||||
<t t-if="editable or request.session.debug">
|
||||
<t t-call="website.http_error_debug"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="container">
|
||||
<t t-call="website.error_message"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="403">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<h1 class="mt-5">403: Forbidden</h1>
|
||||
<p>The page you were looking for could not be authorized.</p>
|
||||
</div>
|
||||
<t t-if="editable or request.session.debug">
|
||||
<t t-call="website.http_error_debug"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="container">
|
||||
<t t-call="website.error_message"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="404">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<t t-raw="0"/>
|
||||
<div class="oe_structure oe_empty">
|
||||
<div class="container">
|
||||
<h1 class="mt-5">404: Page not found!</h1>
|
||||
<p>
|
||||
The page you were looking for could not be found; it is possible you have
|
||||
typed the address incorrectly, but it has most probably been removed due
|
||||
to the recent website reorganisation.
|
||||
</p>
|
||||
<p>Maybe you were looking for one of these popular pages ?</p>
|
||||
<ul>
|
||||
<li><a href="/">Homepage</a></li>
|
||||
<li><a href="/contactus">Contact Us</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="500">
|
||||
<template id="qweb_500" inherit_id="http_routing.500">
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<!-- This template should not use any variable except those provided by website.ir_http._handle_exception -->
|
||||
<!-- no request.crsf_token, no theme style, no assets, ... cursor can be broken during rendering ! -->
|
||||
<!-- see test_05_reset_specific_view_controller_broken_request -->
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<html>
|
||||
<head>
|
||||
<title t-esc="status_message">Internal Server Error</title>
|
||||
<t t-set="debug" t-value="True"/>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css"/>
|
||||
<script src="/web/static/lib/jquery/jquery.js" type="text/javascript"/>
|
||||
<script type="text/javascript" src="/web/static/lib/bootstrap/js/util.js"/>
|
||||
<script type="text/javascript" src="/web/static/lib/bootstrap/js/collapse.js"/>
|
||||
<script type="text/javascript" src="/web/static/lib/bootstrap/js/modal.js"/>
|
||||
<style>
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<t t-if='view'>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var button = $('.reset_templates_button');
|
||||
button.click(function() {
|
||||
$('#reset_templates_mode').val($(this).data('mode'));
|
||||
var dialog = $('#reset_template_confirmation').modal('show');
|
||||
var input = dialog.find('input[type="text"]').val('').focus();
|
||||
var dialog_form = dialog.find('form');
|
||||
dialog_form.submit(function() {
|
||||
if (input.val() == dialog.find('.confirm_word').text()) {
|
||||
dialog.modal('hide');
|
||||
button.prop('disabled', true).text('Working...');
|
||||
$('#reset_templates_form').attr('action', '/website/reset_template');
|
||||
$('#reset_templates_form').trigger('submit');
|
||||
} else {
|
||||
input.val('').focus();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
<xpath expr="//script[last()]" position="before">
|
||||
<script type="text/javascript" src="/web/static/lib/bootstrap/js/modal.js"/>
|
||||
</xpath>
|
||||
<xpath expr="//style" position="after">
|
||||
<t t-if='view'>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var button = $('.reset_templates_button');
|
||||
button.click(function() {
|
||||
$('#reset_templates_mode').val($(this).data('mode'));
|
||||
var dialog = $('#reset_template_confirmation').modal('show');
|
||||
var input = dialog.find('input[type="text"]').val('').focus();
|
||||
var dialog_form = dialog.find('form');
|
||||
dialog_form.submit(function() {
|
||||
if (input.val() == dialog.find('.confirm_word').text()) {
|
||||
dialog.modal('hide');
|
||||
button.prop('disabled', true).text('Working...');
|
||||
$('#reset_templates_form').attr('action', '/website/reset_template');
|
||||
$('#reset_templates_form').trigger('submit');
|
||||
} else {
|
||||
input.val('').focus();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
</t>
|
||||
</head>
|
||||
<body>
|
||||
<div t-if="view" role="dialog" id="reset_template_confirmation" class="modal" tabindex="-1" t-ignore="true">
|
||||
<div class="modal-dialog">
|
||||
<form role="form">
|
||||
<div class="modal-content">
|
||||
<header class="modal-header">
|
||||
<h4 class="modal-title">Reset templates</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>
|
||||
</header>
|
||||
<main class="modal-body">
|
||||
<div class="form-group row mb0">
|
||||
<label for="page-name" class="col-md-12 col-form-label">
|
||||
<p>The selected templates will be reset to their factory settings.</p>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group row mb0">
|
||||
<label for="page-name" class="col-md-9 col-form-label">
|
||||
<p>Type '<i class="confirm_word">yes</i>' in the box below if you want to confirm.</p>
|
||||
</label>
|
||||
<div class="col-md-3 mt16">
|
||||
<input type="text" id="page-name" class="form-control" required="required" placeholder="yes"/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="modal-footer">
|
||||
<button type="button" class="btn" data-dismiss="modal" aria-label="Cancel">Cancel</button>
|
||||
<input type="submit" value="Confirm" class="btn btn-primary"/>
|
||||
</footer>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="wrapwrap">
|
||||
<header>
|
||||
<div class="navbar navbar-expand-md navbar-light bg-light">
|
||||
<div class="container">
|
||||
<div class="collapse navbar-collapse navbar-top-collapse">
|
||||
<ul class="navbar-nav ml-auto" id="top_menu">
|
||||
<li class="nav-item"><a href="/" class="nav-link">Home</a></li>
|
||||
<li class="nav-item"><a href="javascript: window.history.back()" class="nav-link">Back</a></li>
|
||||
</ul>
|
||||
});
|
||||
</script>
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='wrapwrap']" position="before">
|
||||
<div t-if="view" role="dialog" id="reset_template_confirmation" class="modal" tabindex="-1" t-ignore="true">
|
||||
<div class="modal-dialog">
|
||||
<form role="form">
|
||||
<div class="modal-content">
|
||||
<header class="modal-header">
|
||||
<h4 class="modal-title">Reset templates</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>
|
||||
</header>
|
||||
<main class="modal-body">
|
||||
<div class="form-group row mb0">
|
||||
<label for="page-name" class="col-md-12 col-form-label">
|
||||
<p>The selected templates will be reset to their factory settings.</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row mb0">
|
||||
<label for="page-name" class="col-md-9 col-form-label">
|
||||
<p>Type '<i class="confirm_word">yes</i>' in the box below if you want to confirm.</p>
|
||||
</label>
|
||||
<div class="col-md-3 mt16">
|
||||
<input type="text" id="page-name" class="form-control" required="required" placeholder="yes"/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="modal-footer">
|
||||
<button type="button" class="btn" data-dismiss="modal" aria-label="Cancel">Cancel</button>
|
||||
<input type="submit" value="Confirm" class="btn btn-primary"/>
|
||||
</footer>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<div class="oe_structure">
|
||||
<h2 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h2>
|
||||
</div>
|
||||
<div class="container" t-if="view and editable">
|
||||
<div class="alert alert-danger" t-if="debug" role="alert">
|
||||
<h4>Template fallback</h4>
|
||||
<p>An error occured while rendering the template <code t-esc="qweb_exception.name"/>.</p>
|
||||
<p>If this error is caused by a change of yours in the templates, you have the possibility to reset the template to its <strong>factory settings</strong>.</p>
|
||||
<form action="#" method="post" id="reset_templates_form">
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<t t-esc="view.name"/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="redirect" t-att-value="request.httprequest.path"/>
|
||||
<input type="hidden" id="reset_templates_view_id" name="view_id" t-att-value="view.id"/>
|
||||
<input type="hidden" id="reset_templates_mode" name="mode"/>
|
||||
<button data-mode="soft" class="reset_templates_button btn btn-info">Restore previous version (soft reset).</button>
|
||||
<button t-if="view.arch_fs" data-mode="hard" class="reset_templates_button btn btn-outline-danger">Reset to initial version (hard reset).</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<t t-if="editable or debug">
|
||||
<t t-call="website.http_error_debug"/>
|
||||
</t>
|
||||
</main>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='error_message']" position="after">
|
||||
<div class="container" t-if="view and editable">
|
||||
<div class="alert alert-danger" t-if="debug" role="alert">
|
||||
<h4>Template fallback</h4>
|
||||
<p>An error occured while rendering the template <code t-esc="qweb_exception.name"/>.</p>
|
||||
<p>If this error is caused by a change of yours in the templates, you have the possibility to reset the template to its <strong>factory settings</strong>.</p>
|
||||
<form action="#" method="post" id="reset_templates_form">
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<t t-esc="view.name"/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="redirect" t-att-value="request.httprequest.path"/>
|
||||
<input type="hidden" id="reset_templates_view_id" name="view_id" t-att-value="view.id"/>
|
||||
<input type="hidden" id="reset_templates_mode" name="mode"/>
|
||||
<button data-mode="soft" class="reset_templates_button btn btn-info">Restore previous version (soft reset).</button>
|
||||
<button t-if="view.arch_fs" data-mode="hard" class="reset_templates_button btn btn-outline-danger">Reset to initial version (hard reset).</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="portal_404" inherit_id="portal.portal_404">
|
||||
<xpath expr="//li" position="before">
|
||||
<li><a href="/">Homepage</a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="robots">
|
||||
|
||||
Reference in New Issue
Block a user