summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
Diffstat (limited to 'addons')
-rw-r--r--addons/test_website/__init__.py4
-rw-r--r--addons/test_website/__manifest__.py24
-rw-r--r--addons/test_website/controllers/__init__.py4
-rw-r--r--addons/test_website/controllers/main.py13
-rw-r--r--addons/test_website/data/test_website_data.xml54
-rw-r--r--addons/test_website/static/src/js/reset_views.test.js103
-rw-r--r--addons/test_website/tests/__init__.py4
-rw-r--r--addons/test_website/tests/test_reset_views.py102
-rw-r--r--addons/test_website/views/templates.xml8
-rw-r--r--addons/website/controllers/main.py36
-rw-r--r--addons/website/models/ir_http.py68
-rw-r--r--addons/website/models/ir_ui_view.py10
-rw-r--r--addons/website/views/website_templates.xml123
13 files changed, 467 insertions, 86 deletions
diff --git a/addons/test_website/__init__.py b/addons/test_website/__init__.py
new file mode 100644
index 00000000000..273ede25ea3
--- /dev/null
+++ b/addons/test_website/__init__.py
@@ -0,0 +1,4 @@
+# -*- encoding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import controllers
diff --git a/addons/test_website/__manifest__.py b/addons/test_website/__manifest__.py
new file mode 100644
index 00000000000..557722971d4
--- /dev/null
+++ b/addons/test_website/__manifest__.py
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+ 'name': 'Website Test',
+ 'version': '1.0',
+ 'category': 'Hidden',
+ 'sequence': 9876,
+ 'summary': 'Website Test, mainly for module install/uninstall tests',
+ 'description': """This module contains tests related to website. Those are
+contained in a separate module as we are testing module install/uninstall/upgrade
+and we don't want to reload the website module every time, including it's possible
+dependencies. Neither we want to add in website module some routes, views and
+models which only purpose is to run tests.""",
+ 'depends': [
+ 'website',
+ ],
+ 'data': [
+ 'views/templates.xml',
+ 'data/test_website_data.xml',
+ ],
+ 'installable': True,
+ 'application': False,
+}
diff --git a/addons/test_website/controllers/__init__.py b/addons/test_website/controllers/__init__.py
new file mode 100644
index 00000000000..5d4b25db9c0
--- /dev/null
+++ b/addons/test_website/controllers/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import main
diff --git a/addons/test_website/controllers/main.py b/addons/test_website/controllers/main.py
new file mode 100644
index 00000000000..fad797d6984
--- /dev/null
+++ b/addons/test_website/controllers/main.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import http
+from odoo.http import request
+from odoo.addons.portal.controllers.web import Home
+
+
+class WebsiteTest(Home):
+
+ @http.route('/test_view', type='http', auth="public", website=True)
+ def test_view(self, **kw):
+ return request.render('test_website.test_view')
diff --git a/addons/test_website/data/test_website_data.xml b/addons/test_website/data/test_website_data.xml
new file mode 100644
index 00000000000..df827d542a3
--- /dev/null
+++ b/addons/test_website/data/test_website_data.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data noupdate="1">
+ <record id="test_view" model="ir.ui.view">
+ <field name="name">Test View</field>
+ <field name="type">qweb</field>
+ <field name="key">test_website.test_view</field>
+ <field name="arch" type="xml">
+ <t name="Test View" priority="29" t-name="test_website.test_view">
+ <t t-call="website.layout">
+ <p>Test View</p>
+ <p>placeholder</p>
+ </t>
+ </t>
+ </field>
+ </record>
+ <record id="test_page_view" model="ir.ui.view">
+ <field name="name">Test Page View</field>
+ <field name="type">qweb</field>
+ <field name="key">test_website.test_page_view</field>
+ <field name="arch" type="xml">
+ <t name="Test Page View" priority="29" t-name="test_website.test_page_view">
+ <t t-call="website.layout">
+ <div id="oe_structure_test_website_page" class="oe_structure oe_empty"/>
+ <p>Test Page View</p>
+ <p>placeholder</p>
+ </t>
+ </t>
+ </field>
+ </record>
+ <record id="test_page" model="website.page">
+ <field name="website_published">True</field>
+ <field name="url">/test_page_view</field>
+ <field name="view_id" ref="test_page_view"/>
+ </record>
+ <record id="test_view_to_be_t_called" model="ir.ui.view">
+ <field name="name">Test View To Be t-called</field>
+ <field name="type">qweb</field>
+ <field name="key">test_website.test_view_to_be_t_called</field>
+ <field name="arch" type="xml">
+ <t name="Test View To Be t-called" priority="29" t-name="test_website.test_view_to_be_t_called">
+ <p>Test View To Be t-called</p>
+ <p>placeholder</p>
+ </t>
+ </field>
+ </record>
+ <template id="test_view_child_broken" inherit_id="test_website.test_view" active="False">
+ <xpath expr="//p[last()]" position="replace">
+ <p>Test View Child Broken</p>
+ <p>placeholder</p>
+ </xpath>
+ </template>
+ </data>
+</odoo>
diff --git a/addons/test_website/static/src/js/reset_views.test.js b/addons/test_website/static/src/js/reset_views.test.js
new file mode 100644
index 00000000000..957cda6d594
--- /dev/null
+++ b/addons/test_website/static/src/js/reset_views.test.js
@@ -0,0 +1,103 @@
+odoo.define('test_website.reset_views', function (require) {
+'use strict';
+
+var tour = require("web_tour.tour");
+var base = require("web_editor.base");
+
+tour.register('test_reset_page_view_complete_flow_part1', {
+ test: true,
+ url: '/test_page_view',
+ wait_for: base.ready()
+},
+ [
+ // 1. Edit the page through Edit Mode, it will COW the view
+ {
+ content: "enter edit mode",
+ trigger: "a[data-action=edit]"
+ },
+ {
+ content: "drop a snippet",
+ trigger: "#oe_snippets .oe_snippet:has(.s_cover) .oe_snippet_thumbnail",
+ // id starting by 'oe_structure..' will actually create an inherited view
+ run: "drag_and_drop #oe_structure_test_website_page",
+ },
+ {
+ content: "save the page",
+ extra_trigger: '#oe_structure_test_website_page .s_cover',
+ trigger: "#web_editor-top-edit button[data-action=save]",
+ },
+ // 2. Edit that COW'd view in the HTML editor to break it.
+ {
+ content: "open customize menu",
+ extra_trigger: "body:not(.editor_enable)",
+ trigger: '#customize-menu > a',
+ },
+ {
+ content: "open html editor",
+ trigger: '#html_editor',
+ },
+ {
+ content: "add a broken t-field in page DOM",
+ trigger: 'div.ace_line .ace_xml:contains("placeholder")',
+ run: function () {
+ ace.edit('ace-view-editor').getSession().insert({row: 4, column: 1}, '<t t-field="not.exist"/>\n');
+ },
+ },
+ {
+ content: "save the html editor",
+ extra_trigger: '.ace_content:contains("not.exist")',
+ trigger: ".o_ace_view_editor button[data-action=save]",
+ }
+
+ // 3. Reset the broken view
+ ]
+);
+
+tour.register('test_reset_page_view_complete_flow_part2', {
+ test: true,
+ url: '/test_page_view',
+ wait_for: base.ready()
+},
+ [
+ {
+ content: "check that the view got fixed",
+ trigger: 'p:containsExact("Test Page View")',
+ run: function () {}, // it's a check
+ },
+ {
+ content: "check that the inherited COW view is still there (created during edit mode)",
+ trigger: '#oe_structure_test_website_page .s_cover',
+ run: function () {}, // it's a check
+ },
+ //4. Now break the inherited view created when dropping a snippet
+ {
+ content: "open customize menu",
+ trigger: '#customize-menu > a',
+ },
+ {
+ content: "open html editor",
+ trigger: '#html_editor',
+ },
+ {
+ content: "select oe_structure view",
+ trigger: '#ace-view-list',
+ run: function () {
+ var viewId = $('#ace-view-list option:contains("oe_structure")').val();
+ $('#ace-view-list').val(viewId).trigger('change');
+ },
+ },
+ {
+ content: "add a broken t-field in page DOM",
+ trigger: 'div.ace_line .ace_xml:contains("oe_structure_test_website_page")',
+ run: function () {
+ ace.edit('ace-view-editor').getSession().insert({row: 4, column: 1}, '<t t-field="not.exist"/>\n');
+ },
+ },
+ {
+ content: "save the html editor",
+ trigger: ".o_ace_view_editor button[data-action=save]",
+ }
+ ]
+);
+
+});
diff --git a/addons/test_website/tests/__init__.py b/addons/test_website/tests/__init__.py
new file mode 100644
index 00000000000..4c32d5f1959
--- /dev/null
+++ b/addons/test_website/tests/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import test_reset_views
diff --git a/addons/test_website/tests/test_reset_views.py b/addons/test_website/tests/test_reset_views.py
new file mode 100644
index 00000000000..047b1abe3cd
--- /dev/null
+++ b/addons/test_website/tests/test_reset_views.py
@@ -0,0 +1,102 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import re
+
+import odoo.tests
+from odoo.tools import mute_logger
+
+
+def break_view(view, fr='<p>placeholder</p>', to='<p t-field="not.exist"/>'):
+ view.arch = view.arch.replace(fr, to)
+
+
+@odoo.tests.common.tagged('post_install', '-at_install')
+class TestWebsiteResetViews(odoo.tests.HttpCase):
+ def do_test(self, name):
+ self.browser_js(
+ "/",
+ "odoo.__DEBUG__.services['web_tour.tour'].run('%s')" % name,
+ "odoo.__DEBUG__.services['web_tour.tour'].tours.%s.ready" % name,
+ login="admin"
+ )
+
+ def fix_it(self, page):
+ self.authenticate("admin", "admin")
+ resp = self.url_open(page)
+ self.assertEqual(resp.status_code, 500, "Waiting 500")
+ self.assertTrue('<button id="reset_templates_button"' in resp.text)
+ data = {'templates': [self.find_template(resp)], 'redirect': page}
+ resp = self.url_open('/website/reset_templates', data)
+ self.assertEqual(resp.status_code, 200, "Waiting 200")
+
+ def find_template(self, response):
+ find = re.search(r'<input.*type="checkbox".*name="templates".*value="([0-9]+)?"', response.text)
+ return find and find.group(1)
+
+ def setUp(self):
+ super(TestWebsiteResetViews, self).setUp()
+ self.Website = self.env['website']
+ 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')
+ 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')])
+ # Trigger COW then break the QWEB XML on it
+ break_view(self.test_page_view.with_context(website_id=1))
+ 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')
+ 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
+ break_view(self.test_view.with_context(website_id=1))
+ 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')
+ 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')
+
+ total_views = self.View.search_count([('type', '=', 'qweb')])
+ # Trigger COW then break the QWEB XML on it
+ break_view(self.test_view_to_be_t_called.with_context(website_id=1))
+ break_view(self.test_view, to='<t t-call="test_website.test_view_to_be_t_called"/>')
+ 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')
+ def test_04_reset_specific_view_controller_inherit(self):
+ self.test_view_child_broken = self.Website.viewref('test_website.test_view_child_broken')
+
+ # Activate and break the inherited view
+ self.test_view_child_broken.active = True
+ break_view(self.test_view_child_broken.with_context(website_id=1, load_all_views=True))
+
+ 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')
+ # 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
+ # break_view(self.test_view.with_context(website_id=1), to='<t t-esc="request.env[\'website\'].browse(\'a\').name" />')
+ # self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view (1)")
+ # 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')
+ 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
+ break_view(self.test_view.with_context(website_id=1), to='<t t-call="not.exist"/>')
+ 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')
+ def test_07_reset_page_view_complete_flow(self):
+ self.do_test('test_reset_page_view_complete_flow_part1')
+ self.fix_it('/test_page_view')
+ self.do_test('test_reset_page_view_complete_flow_part2')
+ self.fix_it('/test_page_view')
diff --git a/addons/test_website/views/templates.xml b/addons/test_website/views/templates.xml
new file mode 100644
index 00000000000..2ce171bac30
--- /dev/null
+++ b/addons/test_website/views/templates.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <template id="assets_frontend" inherit_id="website.assets_frontend">
+ <xpath expr="script[last()]" position="after">
+ <script type="text/javascript" src="/test_website/static/src/js/reset_views.test.js"></script>
+ </xpath>
+ </template>
+</odoo>
diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py
index 6f1a2df1e8e..81d461830ef 100644
--- a/addons/website/controllers/main.py
+++ b/addons/website/controllers/main.py
@@ -286,24 +286,36 @@ class Website(Home):
views = views.sorted(key=lambda v: (v.inherit_id.id, v.name))
return views.read(['name', 'id', 'key', 'xml_id', 'arch', 'active', 'inherit_id'])
- @http.route('/website/reset_templates', type='http', auth='user', methods=['POST'], website=True)
+ @http.route('/website/reset_templates', type='http', auth='user', methods=['POST'], website=True, csrf=False)
def reset_template(self, templates, redirect='/', **kwargs):
+ """ This method will try to reset a list of broken views ids.
+ It will read the original `arch` from the view's XML file (arch_fs).
+ Views without an `arch_fs` can't be reset, except views created when
+ dropping a snippet in specific oe_structure that create an inherited
+ view doing an xpath.
+ Note: The `arch_fs` field is automatically erased when there is a
+ write on the `arch` field.
+
+ This method is typically useful to reset specific views. In that case we
+ read the XML file from the generic view.
+ """
templates = request.httprequest.form.getlist('templates')
- modules_to_update = []
for temp_id in templates:
view = request.env['ir.ui.view'].browse(int(temp_id))
- if view.page:
+ if 'oe_structure' in view.key:
+ # Particular xpathing view created in edit mode
+ view.unlink()
continue
- view.model_data_id.write({
- 'noupdate': False
- })
- if view.model_data_id.module not in modules_to_update:
- modules_to_update.append(view.model_data_id.module)
+ xml_view = view._get_original_view() # view might already be the xml_view
+ if xml_view.arch_fs:
+ view_file_arch = xml_view.with_context(read_arch_from_file=True).arch
+ # Deactivate COW to not fix a generic view by creating a specific
+ view.with_context(website_id=None).arch_db = view_file_arch
+ if view == xml_view:
+ view.model_data_id.write({
+ 'noupdate': False
+ })
- if modules_to_update:
- modules = request.env['ir.module.module'].sudo().search([('name', 'in', modules_to_update)])
- if modules:
- modules.button_immediate_upgrade()
return request.redirect(redirect)
@http.route(['/website/publish'], type='json', auth="public", website=True)
diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py
index 49e06997692..c45b9ff20fe 100644
--- a/addons/website/models/ir_http.py
+++ b/addons/website/models/ir_http.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
+from lxml import etree
import traceback
import os
import unittest
@@ -12,14 +13,14 @@ import werkzeug.routing
import werkzeug.utils
import odoo
-from odoo import api, models
+from odoo import api, models, registry
from odoo import SUPERUSER_ID
from odoo.http import request
from odoo.tools import config
-from odoo.exceptions import QWebException
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
@@ -185,7 +186,7 @@ class Http(models.AbstractModel):
# 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'] and (not isinstance(exception, QWebException) or not exception.qweb.get('cause')):
+ if 'werkzeug' in config['dev_mode']:
raise e
exception = e
@@ -207,19 +208,9 @@ class Http(models.AbstractModel):
if isinstance(exception, QWebException):
values.update(qweb_exception=exception)
- if isinstance(exception.qweb.get('cause'), odoo.exceptions.AccessError):
+ if type(exception.error) == odoo.exceptions.AccessError:
code = 403
- if code == 500:
- logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
- if 'qweb_exception' in values:
- view = request.env["ir.ui.view"]
- views = view._views_get(exception.qweb['template'])
- to_reset = views.filtered(lambda view: view.model_data_id.noupdate is True and view.arch_fs)
- values['views'] = to_reset
- elif code == 403:
- logger.warn("403 Forbidden:\n\n%s", values['traceback'])
-
values.update(
status_message=werkzeug.http.HTTP_STATUS_CODES[code],
status_code=code,
@@ -233,10 +224,51 @@ class Http(models.AbstractModel):
if not request.uid:
cls._auth_method_public()
- try:
- html = request.env['ir.ui.view'].render_template('website.%s' % view_id, values)
- except Exception:
- html = request.env['ir.ui.view'].render_template('website.http_error', values)
+ with registry(request.env.cr.dbname).cursor() as cr:
+ env = api.Environment(cr, SUPERUSER_ID, request.env.context)
+ if code == 500:
+ logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
+ View = env["ir.ui.view"]
+ if 'qweb_exception' in values:
+ if 'load could not load template' in exception.args:
+ # When t-calling an inexisting template, we don't have reference to
+ # the view that did the t-call. We need to find it.
+ values['views'] = View.search([
+ ('type', '=', 'qweb'),
+ '|',
+ ('arch_db', 'ilike', 't-call="%s"' % exception.name),
+ ('arch_db', 'ilike', "t-call='%s'" % exception.name)
+ ], order='write_date desc', limit=1)
+ else:
+ try:
+ # exception.name might be int, string
+ exception_template = int(exception.name)
+ except:
+ exception_template = exception.name
+ view = View._view_obj(exception_template)
+ et = etree.fromstring(view.with_context(inherit_branding=False).read_combined(['arch'])['arch'])
+ node = et.find(exception.path.replace('/templates/t/', './'))
+ line = node is not None and etree.tostring(node, encoding='unicode')
+ # line = exception.html # FALSE -> contains branding <div t-att-data="request.browse('ok')"/>
+ if line:
+ # If QWebException occurs in a child view, the parent view is raised
+ values['editable'] = request.uid and request.website.is_publisher()
+ values['views'] = View._views_get(exception_template).filtered(
+ lambda v: line in v.arch
+ )
+ else:
+ values['views'] = view
+ # Keep only views that we can reset
+ values['views'] = values['views'].filtered(
+ lambda view: view._get_original_view().arch_fs or 'oe_structure' in view.key
+ )
+ elif code == 403:
+ logger.warn("403 Forbidden:\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
diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py
index 977a986745b..463aed48f84 100644
--- a/addons/website/models/ir_ui_view.py
+++ b/addons/website/models/ir_ui_view.py
@@ -279,6 +279,16 @@ class View(models.Model):
return super(View, self).get_view_id(xml_id)
@api.multi
+ def _get_original_view(self):
+ """Given a view, retrieve the original view it was COW'd from.
+ The given view might already be the original one. In that case it will
+ (and should) return itself.
+ """
+ self.ensure_one()
+ domain = [('key', '=', self.key), ('model_data_id', '!=', None)]
+ return self.search(domain, limit=1) # Useless limit has multiple xmlid should not be possible
+
+ @api.multi
def render(self, values=None, engine='ir.qweb', minimal_qcontext=False):
""" Render the template. If website is enabled on request, then extend rendering context with website values. """
new_context = dict(self._context)
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index 9abc878412e..cc7cff9ca93 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -870,17 +870,14 @@
</h4>
<div id="error_qweb" class="collapse">
<div class="card-body">
- <p t-if="exception.qweb.get('message')">
+ <p t-if="exception.message">
<strong>Error message:</strong>
- <pre t-esc="exception.qweb.get('message')"/>
+ <pre t-esc="exception.message"/>
</p>
<p>
- The error occured while rendering the template <code t-esc="qweb_exception.qweb.get('template')"/>
- <t t-if="'expression' in qweb_exception.qweb">and evaluating the following expression: <code t-esc="qweb_exception.qweb['expression']"/></t>
+ 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>
- <t t-if="'node' in qweb_exception.qweb">
- <pre id="exception_node" t-esc="qweb_exception.pretty_xml()"/>
- </t>
</div>
</div>
</div>
@@ -944,62 +941,74 @@
</template>
<template id="500">
- <!-- This template should not use any variable except those provided by website.ir_http._handle_exception -->
+ <!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
+ <!-- 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"/>
- <t t-call-assets="web.assets_common" t-js="false"/>
- <t t-call-assets="web.assets_frontend" t-js="false"/>
- <t t-call-assets="web.assets_common" t-css="false"/>
- <t t-call-assets="web.assets_frontend" t-css="false"/>
- <script>
- $(document).ready(function() {
- var button = $('#reset_templates_button');
- button.click(function() {
- 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').trigger('submit');
- } else {
- input.val('').focus();
- }
+
+ <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"/>
+
+ <t t-if='views'>
+ <script>
+ $(document).ready(function() {
+ var button = $('#reset_templates_button');
+ button.click(function() {
+ 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...');
+ if ($("input[name='templates']:checked").length) {
+ $('#reset_templates_form').attr('action', '/website/reset_templates');
+ $('#reset_templates_form').trigger('submit');
+ }
+ } else {
+ input.val('').focus();
+ }
+ return false;
+ });
return false;
});
- return false;
});
- });
- </script>
+ </script>
+ </t>
</head>
<body>
- <div role="dialog" id="reset_template_confirmation" class="modal" tabindex="-1" t-ignore="true">
+ <div t-if="views" 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">
- <h3 class="modal-title">Reset templates</h3>
- <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-9 col-form-label">
- <p>The selected templates will be reset to their factory settings.</p>
- <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" class="form-control" required="required" placeholder="yes"/>
+ <div class="modal-content">
+ <header class="modal-header">
+ <h3 class="modal-title">Reset templates</h3>
+ <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-9 col-form-label">
+ <p>The selected templates will be reset to their factory settings.</p>
+ <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>
- </div>
- </main>
- <footer class="modal-footer">
- <input type="submit" value="Confirm" class="btn btn-primary"/>
- <button type="button" class="btn" data-dismiss="modal" aria-label="Cancel">Cancel</button>
- </footer>
- </div>
+ </main>
+ <footer class="modal-footer">
+ <input type="submit" value="Confirm" class="btn btn-primary"/>
+ <button type="button" class="btn" data-dismiss="modal" aria-label="Cancel">Cancel</button>
+ </footer>
+ </div>
</form>
</div>
</div>
@@ -1021,18 +1030,20 @@
<div class="oe_structure">
<h1 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h1>
</div>
- <div class="container" t-if="views">
- <div class="alert alert-danger" t-if="qweb_exception and editable" role="alert">
+ <div class="container" t-if="views 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.qweb['template']"/>.</p>
+ <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 one or more templates to their <strong>factory settings</strong>.</p>
- <form action="/website/reset_templates" method="post" id="reset_templates_form">
- <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
+ <form action="#" method="post" id="reset_templates_form">
<ul class="oe_template_fallback">
<li t-foreach="views" t-as="view">
<label>
<input type="checkbox" name="templates" t-att-value="view.id" t-att-checked="'checked' if view_first else None"/>
<t t-esc="view.name"/>
+ <span t-if="not view._get_original_view()" class="text-danger">
+ (!!! This view will be removed )
+ </span>
</label>
</li>
</ul>