[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:
fja-odoo
2019-10-02 06:59:15 +00:00
parent 92db71ccc4
commit 423402f1e5
10 changed files with 405 additions and 358 deletions
@@ -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."""
+1
View File
@@ -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'],
+87 -1
View File
@@ -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>
+9
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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')])
+40 -106
View File
@@ -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,
+88 -240
View File
@@ -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">