Files
odoo_source/addons/web/views/webclient_templates.xml
T
Samuel Degueldre c989ff339d [IMP] web: report module loading errors in the DOM
Previously, if there were module loading errors, they would typically
happen before the error service was ready, and so there is almost always
no user-facing error that shows up even though the application state may
be severely corrupted. This can be very confusing for developers who are
not used to working with JS and might not check the devtools console.

This commit makes it so that the module system will replace the contents
of the body with an error when some modules were unable to be loaded,
with the list of modules and the reason, so that the developer is not
confused as to why things aren't working as expected.

closes odoo/odoo#104155

Signed-off-by: Samuel Degueldre <sad@odoo.com>
2022-10-27 15:41:15 +02:00

344 lines
17 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Call this template instead of "web.assets_tests" to have the proper conditional check -->
<template id="conditional_assets_tests" name="Tests Assets Bundle">
<t t-if="'tests' in debug or test_mode_enabled">
<t t-call-assets="web.assets_tests" defer_load="True" />
<!-- The assets_tests bundle contains a lot of module whose dependencies are in other bundles that may or may not be loaded. This global is used to silence module loading errors when this bundle is present -->
<script>window.__odooIgnoreModuleErrors = true</script>
</t>
</template>
<template id="web.layout" name="Web layout">&lt;!DOCTYPE html&gt;
<html t-att="html_data or {}">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<title t-esc="title or 'Odoo'"/>
<link type="image/x-icon" rel="shortcut icon" t-att-href="x_icon or '/web/static/img/favicon.ico'"/>
<script id="web.layout.odooscript" type="text/javascript">
var odoo = {
csrf_token: "<t t-nocache="The csrf token must always be up to date." t-esc="request.csrf_token(None)"/>",
debug: "<t t-esc="debug"/>",
};
</script>
<t t-out="head or ''"/>
</head>
<body t-att-class="body_classname">
<t t-out="0"/>
</body>
</html>
</template>
<template id="web.frontend_layout" name="Frontend Layout" inherit_id="web.layout" primary="True">
<xpath expr="//head/meta[last()]" position="after">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
</xpath>
<xpath expr="//head/link[last()]" position="after">
<link rel="preload" href="/web/static/src/libs/fontawesome/fonts/fontawesome-webfont.woff2?v=4.7.0" as="font" crossorigin=""/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
</xpath>
<xpath expr="//head/script[@id='web.layout.odooscript']" position="after">
<script t-nocache="Session information should always be up to date." type="text/javascript">
odoo.__session_info__ = <t t-out="json.dumps(request.env['ir.http'].get_frontend_session_info())"/>;
if (!/(^|;\s)tz=/.test(document.cookie)) {
const userTZ = Intl.DateTimeFormat().resolvedOptions().timeZone;
document.cookie = `tz=${userTZ}; path=/`;
}
</script>
<t t-call-assets="web.assets_frontend_minimal" t-css="false" defer_load="True"/>
<t t-call="web.conditional_assets_tests"/>
<t t-call-assets="web.assets_frontend_lazy" t-css="false" lazy_load="True"/>
</xpath>
<xpath expr="//t[@t-out='0']" position="replace">
<div id="wrapwrap" t-attf-class="#{pageName or ''}">
<header t-if="not no_header" id="top" data-anchor="true">
<img class="img-responsive d-block mx-auto"
t-attf-src="/web/binary/company_logo"
alt="Logo"/>
</header>
<main>
<t t-out="0"/>
</main>
<footer t-cache="no_footer,no_copyright" t-if="not no_footer" id="bottom" data-anchor="true" t-attf-class="bg-light o_footer">
<div id="footer"/>
<div t-if="not no_copyright" class="o_footer_copyright">
<div class="container py-3">
<div class="row">
<div class="col-sm text-center text-sm-start text-muted">
<span class="o_footer_copyright_name me-2">Copyright &amp;copy; <span t-field="res_company.name" itemprop="name">Company name</span></span>
</div>
<div class="col-sm text-center text-sm-end">
<t t-call="web.brand_promotion"/>
</div>
</div>
</div>
</div>
</footer>
</div>
</xpath>
</template>
<template id="brand_promotion_message" name="Brand Promotion Message">
<t t-set="odoo_logo">
<a target="_blank"
t-attf-href="http://www.odoo.com?utm_source=db&amp;utm_medium=#{_utm_medium}"
class="badge text-bg-light">
<img alt="Odoo"
src="/web/static/img/odoo_logo_tiny.png"
width="62" height="20"
style="width: auto; height: 1em; vertical-align: baseline;"/>
</a>
</t>
<t t-set="final_message">Powered by %s%s</t>
<t t-out="final_message % (odoo_logo, _message and ('- ' + _message) or '')"/>
</template>
<template id="brand_promotion" name="Brand Promotion">
<div class="o_brand_promotion">
<t t-call="web.brand_promotion_message">
<t t-set="_message"></t>
<t t-set="_utm_medium" t-valuef="portal"/>
</t>
</div>
</template>
<template id="web.login_layout" name="Login Layout">
<t t-call="web.frontend_layout">
<t t-set="html_data" t-value="{'style': 'height: 100%;'}"/>
<t t-set="body_classname" t-value="'bg-100'"/>
<t t-set="no_header" t-value="True"/>
<t t-set="no_footer" t-value="True"/>
<div class="container py-5">
<div t-attf-class="card border-0 mx-auto bg-100 {{login_card_classes}} o_database_list" style="max-width: 300px;">
<div class="card-body">
<div t-attf-class="text-center pb-3 border-bottom {{'mb-3' if form_small else 'mb-4'}}">
<img t-attf-src="/web/binary/company_logo{{ '?dbname='+db if db else '' }}" alt="Logo" style="max-height:120px; max-width: 100%; width:auto"/>
</div>
<t t-out="0"/>
<div class="text-center small mt-4 pt-3 border-top" t-if="not disable_footer">
<t t-if="not disable_database_manager">
<a class="border-end pe-2 me-1" href="/web/database/manager">Manage Databases</a>
</t>
<a href="https://www.odoo.com?utm_source=db&amp;utm_medium=auth" target="_blank">Powered by <span>Odoo</span></a>
</div>
</div>
</div>
</div>
</t>
</template>
<template id="web.login" name="Login">
<t t-call="web.login_layout">
<form class="oe_login_form" role="form" t-attf-action="/web/login" method="post" onsubmit="this.action = '/web/login' + location.hash">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<div class="mb-3" t-if="databases and len(databases) &gt; 1">
<label for="db" class="col-form-label">Database</label>
<div t-attf-class="input-group {{'input-group-sm' if form_small else ''}}">
<input type="text" name="db" t-att-value="request.db" id="db" t-attf-class="form-control #{'form-control-sm' if form_small else ''}" required="required" readonly="readonly"/>
<a role="button" href="/web/database/selector" class="btn btn-secondary">Select <i class="fa fa-database" role="img" aria-label="Database" title="Database"></i></a>
</div>
</div>
<div class="mb-3 field-login">
<label for="login" class="form-label">Email</label>
<input type="text" placeholder="Email" name="login" t-att-value="login" id="login" t-attf-class="form-control #{'form-control-sm' if form_small else ''}" required="required" autofocus="autofocus" autocapitalize="off"/>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" placeholder="Password" name="password" id="password" t-attf-class="form-control #{'form-control-sm' if form_small else ''}" required="required" autocomplete="current-password" t-att-autofocus="'autofocus' if login else None" maxlength="4096"/>
</div>
<p class="alert alert-danger" t-if="error" role="alert">
<t t-esc="error"/>
</p>
<p class="alert alert-success" t-if="message" role="status">
<t t-esc="message"/>
</p>
<div t-attf-class="clearfix oe_login_buttons text-center gap-1 d-grid mb-1 {{'pt-2' if form_small else 'pt-3'}}">
<button type="submit" class="btn btn-primary">Log in</button>
<t t-if="debug">
<button type="submit" name="redirect" value="/web/become" class="btn btn-link btn-sm">Log in as superuser</button>
</t>
<div class="o_login_auth"/>
</div>
<input type="hidden" name="redirect" t-att-value="redirect"/>
</form>
</t>
</template>
<template id="web.login_successful" name="Login successful">
<t t-call="web.login_layout">
<div class="oe_login_form">
<p>
You are logged in.
</p>
<div t-attf-class="clearfix oe_login_buttons text-center mb-1 pt-3">
<a class="btn btn-primary btn-block" href="/web/session/logout">Log out</a>
</div>
</div>
</t>
</template>
<template id="web.test_helpers">
<t t-call-assets="web.tests_assets" t-js="False"/>
<style>
body {
position: relative; /* bootstrap-datepicker needs this */
}
body:not(.debug) .modal-backdrop, body:not(.debug) .modal, body:not(.debug) .ui-autocomplete {
opacity: 0 !important;
}
#qunit-testrunner-toolbar label {
font-weight: inherit;
margin-bottom: inherit;
}
#qunit-testrunner-toolbar input[type=text] {
width: inherit;
display: inherit;
}
</style>
<t t-call-assets="web.tests_assets" t-css="False"/>
</template>
<template id="web.qunit_suite">
<t t-call="web.layout">
<t t-set="html_data" t-value="{'style': 'height: 100%;'}"/>
<t t-set="title">Web Tests</t>
<t t-set="head">
<t t-call-assets="web.assets_common" t-js="false"/>
<t t-call-assets="web.assets_backend" t-js="false"/>
<t t-call-assets="web.tests_assets_common" t-css="false"/>
<t t-call-assets="web.assets_backend" t-css="false"/>
<t t-call="web.test_helpers"/>
<t t-call-assets="web.qunit_suite_tests" t-js="false"/>
<t t-call-assets="web.qunit_suite_tests" t-css="false"/>
</t>
<div id="qunit"/>
<div id="qunit-fixture"/>
</t>
</template>
<template id="web.qunit_mobile_suite">
<t t-call="web.layout">
<t t-set="html_data" t-value="{'style': 'height: 100%;'}"/>
<t t-set="title">Web Mobile Tests</t>
<t t-set="head">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
<t t-call-assets="web.assets_common" t-js="false"/>
<t t-call-assets="web.assets_backend" t-js="false"/>
<t t-call-assets="web.tests_assets_common" t-css="false"/>
<t t-call-assets="web.assets_backend" t-css="false"/>
<t t-call="web.test_helpers"/>
<t t-call-assets="web.qunit_mobile_suite_tests" t-js="false"/>
<t t-call-assets="web.qunit_mobile_suite_tests" t-css="false"/>
</t>
<div id="qunit"/>
<div id="qunit-fixture"/>
</t>
</template>
<template id="web.benchmark_suite">
<t t-call="web.layout">
<t t-set="html_data" t-value="{'style': 'height: 100%;'}"/>
<t t-set="title">Web Benchmarks</t>
<t t-set="head">
<script type="text/javascript" src="/web/static/lib/benchmarkjs/lodash.js"></script>
<script type="text/javascript" src="/web/static/lib/benchmarkjs/benchmark.js"></script>
<t t-call-assets="web.assets_common" t-js="false"/>
<t t-call-assets="web.assets_backend" t-js="false"/>
<t t-call-assets="web.assets_common" t-css="false"/>
<t t-call-assets="web.assets_backend" t-css="false"/>
<t t-call="web.test_helpers"/>
<script type="text/javascript">
QUnit.config.hidepassed = false;
</script>
<style>
body:not(.debug) .modal-backdrop, body:not(.debug) .modal, body:not(.debug) .ui-autocomplete {
opacity: 0 !important;
}
#qunit-testrunner-toolbar label {
font-weight: inherit;
margin-bottom: inherit;
}
#qunit-testrunner-toolbar input[type=text] {
width: inherit;
display: inherit;
}
</style>
<script type="text/javascript" src="/web/static/tests/views/list_benchmarks.js"></script>
<script type="text/javascript" src="/web/static/tests/views/kanban_benchmarks.js"></script>
<script type="text/javascript" src="/web/static/tests/views/form_benchmarks.js"></script>
</t>
<div id="qunit"/>
<div id="qunit-fixture"/>
</t>
</template>
<template id="web.webclient_bootstrap">
<t t-call="web.layout">
<t t-set="head_web">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
<!-- Chrome, Firefox OS and Opera -->
<meta name="theme-color" content="#875A7B"/>
<link rel="icon" sizes="192x192" href="/web/static/img/mobile-icons/android-192x192.png"/>
<!-- iOS Safari -->
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<link rel="apple-touch-icon" href="/web/static/img/mobile-icons/apple-152x152.png"/>
<!-- Windows Phone -->
<meta name="msapplication-navbutton-color" content="#875A7B"/>
<meta name="msapplication-TileColor" content="#875A7B"/>
<meta name="msapplication-TileImage" content="/web/static/img/mobile-icons/windows-144x144.png"/>
<script type="text/javascript">
odoo.__session_info__ = <t t-out="json.dumps(session_info)"/>;
odoo.reloadMenus = () => fetch(`/web/webclient/load_menus/${odoo.__session_info__.cache_hashes.load_menus}`).then(res => res.json());
odoo.loadMenusPromise = odoo.reloadMenus();
// Block to avoid leaking variables in the script scope
{
const { user_context, cache_hashes } = odoo.__session_info__;
// Prefetch translations to speedup webclient. This is done in JS because link rel="prefetch"
// is not yet supported on safari.
fetch(`/web/webclient/translations/${cache_hashes.translations}?lang=${user_context.lang}`);
}
</script>
<t t-if="request.httprequest.cookies.get('color_scheme') == 'dark'">
<t t-call-assets="web.dark_mode_assets_common" t-js="false"/>
<t t-call-assets="web.dark_mode_assets_backend" t-js="false"/>
</t>
<t t-else="">
<t t-call-assets="web.assets_common" t-js="false"/>
<t t-call-assets="web.assets_backend" t-js="false"/>
</t>
<t t-call-assets="web.assets_common" t-css="false"/>
<t t-call-assets="web.assets_backend" t-css="false"/>
<t t-call-assets="web.assets_backend_prod_only" t-css="false"/>
<t t-call="web.conditional_assets_tests"/>
</t>
<t t-set="head" t-value="head_web + (head or '')"/>
<t t-set="body_classname" t-value="'o_web_client'"/>
</t>
</template>
</odoo>