[IMP] web, http, http_routing, *: new asset for test files
Coming with https://github.com/odoo/enterprise/pull/4281 This commit goal is to encapsulate every tour-test into a separate assets bundle that would be called only during tests (command line) or by URL (debug=tests). That way, a lot of .js files would not be loaded anymore uselessly outside test mode and will speed up the page loads (especially in the frontend as the backend do not reload the page anyway). In order to do that, we needed to propagate the `debug` state from page to page. Otherwise, every page change during a test would simply lose the debug mode and the test would stop as the test assets would not be loaded. As we could not add the `&debug=tests` on every link (either hardcoded or preprocess during the rendering), it has been decided to store it in session. Technical summary: 1. `debug` state (currently only stored in URL) will be stored in session. Either when adding the `debug` param in URL (handled with _dispatch) or by starting Odoo with `test-enable` or `test-file` (handled by session init). 2. Once activated (and so set in session), debug mode will be remain activated even if not visible in URL (after a page navigation eg). To remove it, set its value to nothing, `debug=`. That will exit debug mode (whatever mode it is: debug, assets, tests). 3. As tour-test files are now in a separate bundle, every layout (when needed) should `t-call="web.compiled_assets_tests"`. As it would be redundant and verbose, no `t-if` is needed on the t-call to load it only in test debug mode. That will be handled by `compiled_assets_tests` that will actually do the conditionnal t-call-assets to web.assets_tests, if tests debug mode is activated. 4. In addition to separating the tour-tests files in a separate bundle, we also moved those files to a specific folder under /static/tests/tours next to QUnit tests. 5. Also, tour files will be moved in a specific folder /static/src/js/tours for cleanness purpose (those files will still be kept in their 'normal' assets as needed outside test mode since it is tours). closes odoo/odoo#33213 Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
This commit is contained in:
@@ -20,14 +20,13 @@
|
||||
<script type="text/javascript" src="/account/static/src/js/reconciliation/reconciliation_action.js"></script>
|
||||
<script type="text/javascript" src="/account/static/src/js/reconciliation/reconciliation_model.js"></script>
|
||||
<script type="text/javascript" src="/account/static/src/js/reconciliation/reconciliation_renderer.js"></script>
|
||||
<script type="text/javascript" src="/account/static/src/js/reconciliation/tour_reconciliation.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/account/static/src/js/account_payment_field.js"></script>
|
||||
<script type="text/javascript" src="/account/static/src/js/mail_activity.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/account/static/src/js/bank_statement.js"></script>
|
||||
<script type="text/javascript" src="/account/static/src/js/section_and_note_fields_backend.js"></script>
|
||||
<script type="text/javascript" src="/account/static/src/js/tour.js"></script>
|
||||
<script type="text/javascript" src="/account/static/src/js/tours/account.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/account/static/src/js/bills_tree_upload.js"/>
|
||||
</xpath>
|
||||
@@ -39,6 +38,12 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_tests" name="Account Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/account/static/tests/tours/reconciliation.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="qunit_suite" name="account tests" inherit_id="web.qunit_suite">
|
||||
<xpath expr="//script[contains(@src, '/web/static/tests/views/kanban_tests.js')]" position="after">
|
||||
<script type="text/javascript" src="/account/static/tests/reconciliation_tests.js"></script>
|
||||
|
||||
@@ -2,6 +2,7 @@ odoo.define('base_import.import', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var session = require('web.session');
|
||||
var time = require('web.time');
|
||||
@@ -414,7 +415,7 @@ var DataImport = AbstractAction.extend({
|
||||
|
||||
var $thing = $();
|
||||
var bind = function (d) {};
|
||||
if (session.debug) {
|
||||
if (config.isDebug()) {
|
||||
$thing = $(QWeb.render('ImportView.create_record_option')).insertAfter(v).hide();
|
||||
bind = function (data) {
|
||||
switch (data.type) {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="assets_backend" name="crm assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/crm/static/src/css/crm.css"/>
|
||||
<script type="text/javascript" src="/crm/static/src/js/tour.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="assets_backend" name="crm assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/crm/static/src/css/crm.css"/>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="assets_tests" name="CRM Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/crm/static/src/js/tours/crm.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
<script type="text/javascript" src="/event_sale/static/src/js/event_configurator_view.js"></script>
|
||||
<script type="text/javascript" src="/event_sale/static/src/js/event_configurator_widget_mixin.js"></script>
|
||||
<script type="text/javascript" src="/event_sale/static/src/js/event_configurator_widget.js"></script>
|
||||
<script type="text/javascript" src="/event_sale/static/src/js/event_configurator_test_ui.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="assets_tests" name="Event Sale Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/event_sale/static/tests/tours/event_configurator_ui.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="qunit_suite" inherit_id="web.qunit_suite" name="event_sale_tests">
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
<odoo>
|
||||
<template id="assets_backend" inherit_id="web.assets_backend" name="event_sale_product_configurator assets backend">
|
||||
<xpath expr="script[last()]" position="after">
|
||||
<script type="text/javascript" src="/event_sale_product_configurator/static/src/js/event_configurator_test_ui.js"></script>
|
||||
<script type="text/javascript" src="/event_sale_product_configurator/static/src/js/product_configurator_widget.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_tests" name="Event Sale Product Configurator Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/event_sale_product_configurator/static/tests/tours/event_configurator_ui.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="assets_backend_expense" name="hr_expense assets" inherit_id="web.assets_backend">
|
||||
<template id="assets_tests" name="HR Expense Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/hr_expense/static/src/js/tour.js"></script>
|
||||
<script type="text/javascript" src="/hr_expense/static/src/js/tours/hr_expense.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/hr_recruitment/static/src/scss/hr_job.scss"/>
|
||||
<script type="text/javascript" src="/hr_recruitment/static/src/js/recruitment.js"></script>
|
||||
<script type="text/javascript" src="/hr_recruitment/static/src/js/tour.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="assets_tests" name="HR Recruitment Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/hr_recruitment/static/src/js/tours/hr_recruitment.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
|
||||
@@ -101,7 +101,7 @@ class Proxy(http.Controller):
|
||||
<p>The list of connected USB devices as seen by the IoTBox</p>
|
||||
"""
|
||||
if debug is None:
|
||||
resp += """(<a href="/hw_proxy/status?debug">debug version</a>)"""
|
||||
resp += """(<a href="/hw_proxy/status?debug=1">debug version</a>)"""
|
||||
devices = subprocess.check_output("lsusb").decode('utf-8').split('\n')
|
||||
count = 0
|
||||
resp += "<div class='devices'>\n"
|
||||
|
||||
@@ -10,7 +10,6 @@ var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var Dialog = require('web.Dialog');
|
||||
var dom = require('web.dom');
|
||||
var session = require('web.session');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
var _t = core._t;
|
||||
@@ -731,7 +730,7 @@ var Discuss = AbstractAction.extend({
|
||||
* @private
|
||||
*/
|
||||
_renderButtons: function () {
|
||||
this.$buttons = $(QWeb.render('mail.discuss.ControlButtons', { debug: session.debug }));
|
||||
this.$buttons = $(QWeb.render('mail.discuss.ControlButtons', { debug: config.isDebug() }));
|
||||
this.$buttons.find('button').css({display:'inline-block'});
|
||||
this.$buttons
|
||||
.on('click', '.o_mail_discuss_button_invite', this._onInviteButtonClicked.bind(this))
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
odoo.define('mail.DebugManager', function (require) {
|
||||
odoo.define('mail.DebugManager.Backend', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var DebugManager = require('web.DebugManager');
|
||||
var DebugManager = require('web.DebugManager.Backend');
|
||||
|
||||
var _t = core._t;
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@ odoo.define('mail.ActivityRenderer', function (require) {
|
||||
|
||||
var AbstractRenderer = require('web.AbstractRenderer');
|
||||
var ActivityRecord = require('mail.ActivityRecord');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var field_registry = require('web.field_registry');
|
||||
var KanbanColumnProgressBar = require('web.KanbanColumnProgressBar');
|
||||
@@ -32,7 +33,7 @@ var ActivityRenderer = AbstractRenderer.extend({
|
||||
init: function (parent, state, params) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.qweb = new qweb(session.debug, {_s: session.origin});
|
||||
this.qweb = new qweb(config.isDebug(), {_s: session.origin});
|
||||
this.qweb.add_template(utils.json_node_to_xml(params.templates));
|
||||
},
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<!-- systray -->
|
||||
<script type="text/javascript" src="/mail/static/src/js/systray/systray_activity_menu.js"></script>
|
||||
<script type="text/javascript" src="/mail/static/src/js/systray/systray_messaging_menu.js"></script>
|
||||
<script type="text/javascript" src="/mail/static/src/js/tour.js"></script>
|
||||
<script type="text/javascript" src="/mail/static/src/js/tours/mail.js"></script>
|
||||
<script type="text/javascript" src="/mail/static/src/js/user_menu.js"></script>
|
||||
<!-- tools -->
|
||||
<script type="text/javascript" src="/mail/static/src/js/tools/debug_manager.js"></script>
|
||||
|
||||
@@ -136,6 +136,7 @@
|
||||
<a target="_blank" class="label label-danger" href="https://www.odoo.com/page/website-builder">Odoo</a>
|
||||
</div>
|
||||
<div class="pull-left text-muted" itemscope="itemscope" itemtype="https://schema.org/Organization">
|
||||
<t t-call="web.debug_icon"/>
|
||||
Copyright &copy; <span t-field="res_company.name" itemprop="name">Company name</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ _logger = logging.getLogger(__name__)
|
||||
class PosController(http.Controller):
|
||||
|
||||
@http.route('/pos/web', type='http', auth='user')
|
||||
def pos_web(self, debug=False, **k):
|
||||
def pos_web(self, **k):
|
||||
# if user not logged in, log him in
|
||||
pos_sessions = request.env['pos.session'].search([
|
||||
('state', '=', 'opened'),
|
||||
|
||||
@@ -293,8 +293,7 @@ var Gui = core.Class.extend({
|
||||
this.chrome.loading_message(_t('Closing ...'));
|
||||
|
||||
this.pos.push_order().then(function(){
|
||||
var url = "/web#action=point_of_sale.action_client_pos_menu";
|
||||
window.location = session.debug ? $.param.querystring(url, {debug: session.debug}) : url;
|
||||
window.location = "/web#action=point_of_sale.action_client_pos_menu";
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ exports.PosModel = Backbone.Model.extend({
|
||||
|
||||
this.proxy_queue = new devices.JobQueue(); // used to prevent parallels communications to the proxy
|
||||
this.db = new PosDB(); // a local database used to search trough products and categories & store pending orders
|
||||
this.debug = config.debug; //debug mode
|
||||
this.debug = config.isDebug(); //debug mode
|
||||
|
||||
// Business data; loaded from the server at launch
|
||||
this.company_logo = null;
|
||||
@@ -2251,7 +2251,7 @@ exports.Order = Backbone.Model.extend({
|
||||
} else {
|
||||
subreceipt = subreceipt.split('\n').slice(1).join('\n');
|
||||
var qweb = new QWeb2.Engine();
|
||||
qweb.debug = config.debug;
|
||||
qweb.debug = config.isDebug();
|
||||
qweb.default_dict = _.clone(QWeb.default_dict);
|
||||
qweb.add_template('<templates><t t-name="subreceipt">'+subreceipt+'</t></templates>');
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/gui.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/popups.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/screens.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/tests.js"></script>
|
||||
</template>
|
||||
|
||||
<template id="wysiwyg_iframe_css_assets" name="CSS assets for wysiwyg iframe content">
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
<template id="assets_backend" name="hr assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/point_of_sale/static/src/scss/pos_dashboard.scss"/>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/tour.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/tours/point_of_sale.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_tests" name="Point Of Sale Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/point_of_sale/static/tests/tours/point_of_sale.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -31,12 +31,14 @@
|
||||
var odoo = {
|
||||
csrf_token: "<t t-esc="request.csrf_token(None)"/>",
|
||||
session_info: <t t-raw="session_info"/>,
|
||||
debug: "<t t-esc="debug"/>",
|
||||
};
|
||||
</script>
|
||||
|
||||
<t t-call-assets="web.assets_common" t-css="false"/>
|
||||
<t t-call-assets="point_of_sale.pos_assets_backend" t-css="false"/>
|
||||
<t t-call-assets="point_of_sale.assets"/>
|
||||
<t t-call="web.conditional_assets_tests"/>
|
||||
|
||||
<script type="text/javascript" id="loading-script" t-raw="init">
|
||||
odoo.define('web.web_client', function (require) {
|
||||
|
||||
@@ -18,10 +18,15 @@
|
||||
</xpath>
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal.js"></script>
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal_tour.js"></script>
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal_chatter.js"></script>
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal_signature.js"></script>
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal_sidebar.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_tests" name="Portal Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/portal/static/tests/tours/portal.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<link rel="stylesheet" href="/project/static/src/css/project.css"/>
|
||||
<script type="text/javascript" src="/project/static/src/js/project.js"></script>
|
||||
<script type="text/javascript" src="/project/static/src/js/project_task_kanban_examples.js"></script>
|
||||
<script type="text/javascript" src="/project/static/src/js/tour.js"></script>
|
||||
<script type="text/javascript" src="/project/static/src/js/tours/project.js"></script>
|
||||
<link rel="stylesheet" type="text/scss" href="/project/static/src/scss/project_dashboard.scss"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<a t-attf-href="/my/project/#{project.id}?{{ keep_query() }}"><span t-field="project.name"/></a>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a t-attf-href="/my/tasks?{{keep_query('debug', filterby=project.id)}}">
|
||||
<a t-attf-href="/my/tasks?{{keep_query(filterby=project.id)}}">
|
||||
<t t-esc="project.task_count" />
|
||||
<t t-esc="project.label_tasks" />
|
||||
</a>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</xpath>
|
||||
<xpath expr="script[last()]" position="after">
|
||||
<script type="text/javascript" src="/sale/static/src/js/sale.js"></script>
|
||||
<script type="text/javascript" src="/sale/static/src/js/tour.js"></script>
|
||||
<script type="text/javascript" src="/sale/static/src/js/tours/sale.js"></script>
|
||||
<script type="text/javascript" src="/sale/static/src/js/account_reconciliation.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
@@ -17,7 +17,11 @@
|
||||
</xpath>
|
||||
<xpath expr="script[last()]" position="after">
|
||||
<script type="text/javascript" src="/sale/static/src/js/sale_portal_sidebar.js"></script>
|
||||
<script type="text/javascript" src="/sale/static/src/js/sale_tour_sale_signature.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="assets_tests" name="Sale Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/sale/static/tests/tours/sale_signature.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="qunit_suite" name="sale_tests" inherit_id="web.qunit_suite">
|
||||
|
||||
@@ -5,19 +5,23 @@
|
||||
<link rel="stylesheet" type="text/scss" href="/sale_product_configurator/static/src/scss/product_configurator_widget.scss"/>
|
||||
</xpath>
|
||||
<xpath expr="script[last()]" position="after">
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/src/js/tour.js"></script>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/src/js/tours/sale_product_configurator.js"></script>
|
||||
<script type="text/javascript" src="/sale/static/src/js/variant_mixin.js"></script>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/src/js/product_configurator_renderer.js"></script>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/src/js/product_configurator_controller.js"></script>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/src/js/product_configurator_view.js"></script>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/src/js/product_configurator_widget.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/src/js/product_configurator_modal.js"></script>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/product_configurator_test_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/product_configurator_advanced_test_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/product_configurator_edition_test_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/product_configurator_single_custom_attribute_test_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/product_configurator_pricelist_test_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/product_configurator_optional_products_test_ui.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="assets_tests" name="Sale Product Configurator Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/tours/product_configurator_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/tours/product_configurator_advanced_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/tours/product_configurator_edition_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/tours/product_configurator_single_custom_attribute_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/tours/product_configurator_pricelist_ui.js"/>
|
||||
<script type="text/javascript" src="/sale_product_configurator/static/tests/tours/product_configurator_optional_products_ui.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="qunit_suite" inherit_id="web.qunit_suite" name="sale_product_configurator_tests">
|
||||
|
||||
@@ -10,7 +10,7 @@ import json
|
||||
class BarcodeController(http.Controller):
|
||||
|
||||
@http.route(['/stock/barcode/'], type='http', auth='user')
|
||||
def a(self, debug=False, **k):
|
||||
def a(self, **k):
|
||||
if not request.session.uid:
|
||||
return http.local_redirect('/web/login?redirect=/stock/barcode/')
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ var tour = require('web_tour.tour');
|
||||
|
||||
tour.register('stock', {
|
||||
test: true,
|
||||
url: '/web?debug=assets#action=stock.action_stock_config_settings',
|
||||
url: '/web#action=stock.action_stock_config_settings',
|
||||
},
|
||||
[
|
||||
{
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
<template id="stock_assets_backend" name="mrp_workorder assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/stock/static/tests/route_tour.js"></script>
|
||||
<script type="text/javascript" src="/stock/static/src/js/inventory_singleton_list_controller.js"></script>
|
||||
<script type="text/javascript" src="/stock/static/src/js/inventory_singleton_list_view.js"></script>
|
||||
<script type="text/javascript" src="/stock/static/src/js/inventory_validate_button_controller.js"></script>
|
||||
@@ -20,6 +19,12 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_tests" name="Stock Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/stock/static/tests/tours/route.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="message_head">
|
||||
<t t-if="move.state != 'done'">
|
||||
<strong>The initial demand has been updated.</strong>
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
<script type="text/javascript" src="/survey/static/src/js/survey_result.js" />
|
||||
<script type="text/javascript" src="/survey/static/src/js/survey.js" />
|
||||
<script type="text/javascript" src="/survey/static/src/js/survey_timer.js" />
|
||||
<script type="text/javascript" src="/survey/static/src/js/tour_test_survey.js"></script>
|
||||
<script type="text/javascript" src="/survey/static/src/js/tour_test_certification_success.js"></script>
|
||||
<script type="text/javascript" src="/survey/static/src/js/tour_test_certification_failure.js"></script>
|
||||
<script type="text/javascript" src="/survey/static/src/js/tour_test_survey_prefill.js"></script>
|
||||
|
||||
<link href="/survey/static/src/css/survey_print.css" rel="stylesheet" type="text/css"/>
|
||||
<link href="/survey/static/src/css/survey_result.css" rel="stylesheet" type="text/css"></link>
|
||||
@@ -36,5 +32,14 @@
|
||||
<link rel="stylesheet" type="text/scss" href="/survey/static/src/scss/survey_views.scss"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_tests" name="Survey Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/survey/static/tests/tours/certification_failure.js"></script>
|
||||
<script type="text/javascript" src="/survey/static/tests/tours/certification_success.js"></script>
|
||||
<script type="text/javascript" src="/survey/static/tests/tours/survey.js"></script>
|
||||
<script type="text/javascript" src="/survey/static/tests/tours/survey_prefill.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -1,8 +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>
|
||||
<template id="assets_tests" name="Test Website Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/test_website/static/tests/tours/reset_views.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -485,7 +485,7 @@ class Home(http.Controller):
|
||||
:param unique: this parameters is not used, but mandatory: it is used by the HTTP stack to make a unique request
|
||||
:return: the menus (including the images in Base64)
|
||||
"""
|
||||
menus = request.env["ir.ui.menu"].load_menus(request.debug)
|
||||
menus = request.env["ir.ui.menu"].load_menus(request.session.debug)
|
||||
body = json.dumps(menus, default=ustr)
|
||||
response = request.make_response(body, [
|
||||
# this method must specify a content-type application/json instead of using the default text/html set because
|
||||
@@ -542,12 +542,6 @@ class Home(http.Controller):
|
||||
if not odoo.tools.config['list_db']:
|
||||
values['disable_database_manager'] = True
|
||||
|
||||
# otherwise no real way to test debug mode in template as ?debug =>
|
||||
# values['debug'] = '' but that's also the fallback value when
|
||||
# missing variables in qweb
|
||||
if 'debug' in values:
|
||||
values['debug'] = True
|
||||
|
||||
response = request.render('web.login', values)
|
||||
response.headers['X-Frame-Options'] = 'DENY'
|
||||
return response
|
||||
@@ -1590,8 +1584,7 @@ class Apps(http.Controller):
|
||||
action['views'] = [(False, u'form')]
|
||||
|
||||
sakey = Session().save_session_action(action)
|
||||
debug = '?debug' if req.debug else ''
|
||||
return werkzeug.utils.redirect('/web{0}#sa={1}'.format(debug, sakey))
|
||||
return werkzeug.utils.redirect('/web#sa={0}'.format(sakey))
|
||||
|
||||
|
||||
class ReportController(http.Controller):
|
||||
|
||||
@@ -17,7 +17,7 @@ class Http(models.AbstractModel):
|
||||
|
||||
def webclient_rendering_context(self):
|
||||
return {
|
||||
'menu_data': request.env['ir.ui.menu'].load_menus(request.debug),
|
||||
'menu_data': request.env['ir.ui.menu'].load_menus(request.session.debug),
|
||||
'session_info': json.dumps(self.session_info()),
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class Http(models.AbstractModel):
|
||||
lang = user_context.get("lang")
|
||||
translations_per_module, _ = request.env['ir.translation'].get_translations_for_webclient(mods, lang)
|
||||
|
||||
menu_json_utf8 = json.dumps(request.env['ir.ui.menu'].load_menus(request.debug), default=ustr, sort_keys=True).encode()
|
||||
menu_json_utf8 = json.dumps(request.env['ir.ui.menu'].load_menus(request.session.debug), default=ustr, sort_keys=True).encode()
|
||||
translations_json_utf8 = json.dumps(translations_per_module, sort_keys=True).encode()
|
||||
|
||||
return {
|
||||
|
||||
@@ -2,6 +2,7 @@ odoo.define('web.Apps', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var framework = require('web.framework');
|
||||
var session = require('web.session');
|
||||
@@ -111,8 +112,8 @@ var Apps = AbstractAction.extend({
|
||||
self.client = client;
|
||||
|
||||
var qs = {db: client.dbname};
|
||||
if (session.debug) {
|
||||
qs.debug = session.debug;
|
||||
if (config.isDebug()) {
|
||||
qs.debug = odoo.debug;
|
||||
}
|
||||
var u = $.param.querystring(client.origin + "/apps/embed/client", qs);
|
||||
var css = {width: '100%', height: '750px'};
|
||||
|
||||
@@ -38,8 +38,6 @@
|
||||
var commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg;
|
||||
var cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g;
|
||||
|
||||
var debug = /\bdebug\b/.test(window.location.search);
|
||||
|
||||
if (!window.odoo) {
|
||||
window.odoo = {};
|
||||
}
|
||||
@@ -51,7 +49,6 @@
|
||||
});
|
||||
|
||||
odoo.testing = typeof QUnit === 'object';
|
||||
odoo.debug = debug;
|
||||
odoo.remainingJobs = jobs;
|
||||
odoo.__DEBUG__ = {
|
||||
didLogInfo: didLogInfoPromise,
|
||||
|
||||
@@ -527,9 +527,6 @@ var ActionManager = Widget.extend({
|
||||
*/
|
||||
_executeURLAction: function (action, options) {
|
||||
var url = action.url;
|
||||
if (config.debug && url && url.length && url[0] === '/') {
|
||||
url = $.param.querystring(url, {debug: config.debug});
|
||||
}
|
||||
|
||||
if (action.target === 'self') {
|
||||
framework.redirect(url);
|
||||
|
||||
@@ -358,7 +358,7 @@ ActionManager.include({
|
||||
viewID: view[0],
|
||||
Widget: View,
|
||||
});
|
||||
} else if (config.debug === 'assets') {
|
||||
} else if (config.isDebug('assets')) {
|
||||
console.error("View type '" + viewType + "' is not present in the view registry.");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,9 +12,9 @@ odoo.define('web.Loading', function (require) {
|
||||
* After a delay of 3s, if a rpc is still not completed, we also block the UI.
|
||||
*/
|
||||
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var framework = require('web.framework');
|
||||
var session = require('web.session');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var _t = core._t;
|
||||
@@ -54,7 +54,7 @@ var Loading = Widget.extend({
|
||||
|
||||
this.count += increment;
|
||||
if (this.count > 0) {
|
||||
if (session.debug) {
|
||||
if (config.isDebug()) {
|
||||
this.$el.text(_.str.sprintf( _t("Loading (%d)"), this.count));
|
||||
} else {
|
||||
this.$el.text(_t("Loading"));
|
||||
|
||||
@@ -9,6 +9,7 @@ odoo.define('web.UserMenu', function (require) {
|
||||
* editing its preferences, accessing the documentation, logging out...
|
||||
*/
|
||||
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var framework = require('web.framework');
|
||||
var Dialog = require('web.Dialog');
|
||||
@@ -39,7 +40,7 @@ var UserMenu = Widget.extend({
|
||||
return Promise.resolve();
|
||||
}
|
||||
var topbar_name = session.name;
|
||||
if (session.debug) {
|
||||
if (config.isDebug()) {
|
||||
topbar_name = _.str.sprintf("%s (%s)", topbar_name, session.db);
|
||||
}
|
||||
self.$('.oe_topbar_name').text(topbar_name);
|
||||
|
||||
@@ -435,7 +435,7 @@ var loadAsset = (function () {
|
||||
}
|
||||
var params = {
|
||||
args: [xmlId, {
|
||||
debug: config.debug
|
||||
debug: config.isDebug()
|
||||
}],
|
||||
kwargs: {
|
||||
context: odoo.session_info.user_context,
|
||||
|
||||
@@ -3,7 +3,6 @@ odoo.define('web.Session', function (require) {
|
||||
|
||||
var ajax = require('web.ajax');
|
||||
var concurrency = require('web.concurrency');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var local_storage = require('web.local_storage');
|
||||
var mixins = require('web.mixins');
|
||||
@@ -35,7 +34,6 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
this.avoid_recursion = false;
|
||||
this.use_cors = options.use_cors || false;
|
||||
this.setup(origin);
|
||||
this.debug = config.debug;
|
||||
|
||||
// for historic reasons, the session requires a name to properly work
|
||||
// (see the methods get_cookie and set_cookie). We should perhaps
|
||||
@@ -305,9 +303,6 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
var self = this;
|
||||
options = _.clone(options || {});
|
||||
options.headers = _.extend({}, options.headers);
|
||||
if (odoo.debug) {
|
||||
options.headers["X-Debug-Mode"] = $.deparam($.param.querystring()).debug;
|
||||
}
|
||||
|
||||
// we add here the user context for ALL queries, mainly to pass
|
||||
// the allowed_company_ids key
|
||||
|
||||
@@ -8,6 +8,7 @@ odoo.define('web.basic_fields', function (require) {
|
||||
*/
|
||||
|
||||
var AbstractField = require('web.AbstractField');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var crash_manager = require('web.crash_manager');
|
||||
var datepicker = require('web.datepicker');
|
||||
@@ -2770,7 +2771,7 @@ var FieldDomain = AbstractField.extend({
|
||||
this.domainSelector = new DomainSelector(this, this._domainModel, value, {
|
||||
readonly: this.mode === "readonly" || this.inDialog,
|
||||
filters: this.fsFilters,
|
||||
debugMode: session.debug,
|
||||
debugMode: config.isDebug(),
|
||||
});
|
||||
def = this.domainSelector.prependTo(this.$el);
|
||||
} else {
|
||||
@@ -2859,7 +2860,7 @@ var FieldDomain = AbstractField.extend({
|
||||
new DomainSelectorDialog(this, this._domainModel, this.value || "[]", {
|
||||
readonly: this.mode === "readonly",
|
||||
filters: this.fsFilters,
|
||||
debugMode: session.debug,
|
||||
debugMode: config.isDebug(),
|
||||
}).open();
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -10,19 +10,7 @@ odoo.define('web.config', function () {
|
||||
* this file someday.
|
||||
*/
|
||||
|
||||
var debugParam = $.deparam($.param.querystring()).debug;
|
||||
var debug = false;
|
||||
if (debugParam !== undefined) {
|
||||
debug = debugParam === 'assets' ? 'assets' : true;
|
||||
}
|
||||
|
||||
var config = {
|
||||
/**
|
||||
* debug can be either a boolean, or the special value 'assets'
|
||||
*
|
||||
* @type boolean|string
|
||||
*/
|
||||
debug: debug,
|
||||
device: {
|
||||
/**
|
||||
* touch is a boolean, true if the device supports touch interaction
|
||||
@@ -51,6 +39,18 @@ var config = {
|
||||
*/
|
||||
SIZES: { XS: 0, VSM: 1, SM: 2, MD: 3, LG: 4, XL: 5, XXL: 6 },
|
||||
},
|
||||
/**
|
||||
* States whether the current environment is in debug or not.
|
||||
*
|
||||
* @param debugMode the debug mode to check, empty for simple debug mode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isDebug: function (debugMode) {
|
||||
if (debugMode) {
|
||||
return odoo.debug && odoo.debug.indexOf(debugMode) !== -1;
|
||||
}
|
||||
return odoo.debug;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ odoo.define('web.core', function (require) {
|
||||
"use strict";
|
||||
|
||||
var Bus = require('web.Bus');
|
||||
var Class = require('web.Class');
|
||||
var config = require('web.config');
|
||||
var Class = require('web.Class');
|
||||
var QWeb = require('web.QWeb');
|
||||
var Registry = require('web.Registry');
|
||||
var translation = require('web.translation');
|
||||
@@ -27,7 +27,7 @@ _.each('resize,scroll'.split(','), function (evtype) {
|
||||
});
|
||||
|
||||
return {
|
||||
qweb: new QWeb(config.debug),
|
||||
qweb: new QWeb(config.isDebug()),
|
||||
|
||||
// core classes and functions
|
||||
Class: Class,
|
||||
|
||||
@@ -42,7 +42,7 @@ return core.Class.extend({
|
||||
var self = this;
|
||||
var key = this._gen_key(action_id, additional_context || {});
|
||||
|
||||
if (config.debug === 'assets' || !this._cache.actions[key]) {
|
||||
if (config.isDebug('assets') || !this._cache.actions[key]) {
|
||||
this._cache.actions[key] = rpc.query({
|
||||
route: "/web/action/load",
|
||||
params: {
|
||||
@@ -82,7 +82,7 @@ return core.Class.extend({
|
||||
var views_descr = params.views_descr;
|
||||
var key = this._gen_key(model, views_descr, options || {}, context);
|
||||
|
||||
if (config.debug === 'assets' || !this._cache.views[key]) {
|
||||
if (config.isDebug('assets') || !this._cache.views[key]) {
|
||||
// Don't load filters if already in cache
|
||||
var filters_key;
|
||||
if (options.load_filters) {
|
||||
@@ -137,7 +137,7 @@ return core.Class.extend({
|
||||
*/
|
||||
load_filters: function (params) {
|
||||
var key = this._gen_key(params.modelName, params.actionId);
|
||||
if (config.debug === 'assets' || !this._cache.filters[key]) {
|
||||
if (config.isDebug('assets') || !this._cache.filters[key]) {
|
||||
this._cache.filters[key] = rpc.query({
|
||||
args: [params.modelName, params.actionId],
|
||||
kwargs: {
|
||||
|
||||
@@ -1,91 +1,43 @@
|
||||
odoo.define('web.DebugManager', function (require) {
|
||||
"use strict";
|
||||
|
||||
var ActionManager = require('web.ActionManager');
|
||||
var dialogs = require('web.view_dialogs');
|
||||
var startClickEverywhere = require('web.clickEverywhere');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var Dialog = require('web.Dialog');
|
||||
var field_utils = require('web.field_utils');
|
||||
var session = require('web.session');
|
||||
var SystrayMenu = require('web.SystrayMenu');
|
||||
var utils = require('web.utils');
|
||||
var WebClient = require('web.WebClient');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
var _t = core._t;
|
||||
|
||||
/**
|
||||
* DebugManager base + general features (applicable to any context)
|
||||
*/
|
||||
var DebugManager = Widget.extend({
|
||||
template: "WebClient.DebugManager",
|
||||
xmlDependencies: ['/web/static/src/xml/debug.xml'],
|
||||
events: {
|
||||
"click a[data-action]": "perform_callback",
|
||||
"mouseover .o_debug_dropdowns > li:not(.show)": function(e) {
|
||||
// Open other dropdowns on mouseover
|
||||
var $opened = this.$('.o_debug_dropdowns > li.show');
|
||||
if($opened.length) {
|
||||
$opened.removeClass('show');
|
||||
$(e.currentTarget).addClass('show').find('> a').focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
// 15 fps, only actually call after sequences of queries
|
||||
this._update_stats = _.throttle(
|
||||
this._update_stats.bind(this),
|
||||
1000/15, {leading: false});
|
||||
this._events = null;
|
||||
if (document.querySelector('meta[name=debug]')) {
|
||||
this._events = [];
|
||||
}
|
||||
var debug = odoo.debug;
|
||||
this.debug_mode = debug;
|
||||
this.debug_mode_help = debug && debug !== '1' ? ' (' + debug + ')' : '';
|
||||
},
|
||||
start: function () {
|
||||
core.bus.on('rpc:result', this, function (req, resp) {
|
||||
this._debug_events(resp.debug);
|
||||
});
|
||||
this.on('update-stats', this, this._update_stats);
|
||||
var init;
|
||||
if ((init = document.querySelector('meta[name=debug]'))) {
|
||||
this._debug_events(JSON.parse(init.getAttribute('value')));
|
||||
}
|
||||
|
||||
this.$dropdown = this.$(".o_debug_dropdown");
|
||||
// falsy if can't write to user or couldn't find technical features
|
||||
// group, otherwise features group id
|
||||
this._features_group = null;
|
||||
// whether group is currently enabled for current user
|
||||
this._has_features = false;
|
||||
// whether the current user is an administrator
|
||||
this._is_admin = session.is_system;
|
||||
return Promise.resolve(
|
||||
this._rpc({
|
||||
model: 'res.users',
|
||||
method: 'check_access_rights',
|
||||
kwargs: {operation: 'write', raise_exception: false},
|
||||
}),
|
||||
session.user_has_group('base.group_no_one'),
|
||||
this._rpc({
|
||||
model: 'ir.model.data',
|
||||
method: 'xmlid_to_res_id',
|
||||
kwargs: {xmlid: 'base.group_no_one'},
|
||||
}),
|
||||
this._super()
|
||||
).then(function (can_write_user, has_group_no_one, group_no_one_id) {
|
||||
this._features_group = can_write_user && group_no_one_id;
|
||||
this._has_features = has_group_no_one;
|
||||
).then(function () {
|
||||
return this.update();
|
||||
}.bind(this));
|
||||
},
|
||||
leave_debug_mode: function () {
|
||||
var qs = $.deparam.querystring();
|
||||
delete qs.debug;
|
||||
window.location.search = '?' + $.param(qs);
|
||||
}, /**
|
||||
/**
|
||||
* Calls the appropriate callback when clicking on a Debug option
|
||||
*/
|
||||
perform_callback: function (evt) {
|
||||
@@ -102,54 +54,14 @@ var DebugManager = Widget.extend({
|
||||
},
|
||||
|
||||
_debug_events: function (events) {
|
||||
if (!this._events) { return; }
|
||||
if (!this._events) {
|
||||
return;
|
||||
}
|
||||
if (events && events.length) {
|
||||
this._events.push(events);
|
||||
}
|
||||
this.trigger('update-stats', this._events);
|
||||
},
|
||||
requests_clear: function () {
|
||||
if (!this._events) { return; }
|
||||
this._events = [];
|
||||
this.trigger('update-stats', this._events);
|
||||
},
|
||||
_update_stats: function (rqs) {
|
||||
var requests = 0, rtime = 0, queries = 0, qtime = 0;
|
||||
for(var r = 0; r < rqs.length; ++r) {
|
||||
for (var i = 0; i < rqs[r].length; i++) {
|
||||
var event = rqs[r][i];
|
||||
var query_start, request_start;
|
||||
switch (event[0]) {
|
||||
case 'request-start':
|
||||
request_start = event[3] * 1e3;
|
||||
break;
|
||||
case 'request-end':
|
||||
++requests;
|
||||
rtime += (event[3] * 1e3 - request_start) | 0;
|
||||
break;
|
||||
case 'sql-start':
|
||||
query_start = event[3] * 1e3;
|
||||
break;
|
||||
case 'sql-end':
|
||||
++queries;
|
||||
qtime += (event[3] * 1e3 - query_start) | 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.$('#debugmanager_requests_stats').text(
|
||||
_.str.sprintf(_t("%d requests (%d ms) %d queries (%d ms)"),
|
||||
requests, rtime, queries, qtime));
|
||||
},
|
||||
show_timelines: function () {
|
||||
if (this._overlay) {
|
||||
this._overlay.destroy();
|
||||
this._overlay = null;
|
||||
return;
|
||||
}
|
||||
this._overlay = new RequestsOverlay(this);
|
||||
this._overlay.appendTo(document.body);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the debug manager: reinserts all "universal" controls
|
||||
@@ -162,64 +74,13 @@ var DebugManager = Widget.extend({
|
||||
}));
|
||||
return Promise.resolve();
|
||||
},
|
||||
select_view: function () {
|
||||
var self = this;
|
||||
new dialogs.SelectCreateDialog(this, {
|
||||
res_model: 'ir.ui.view',
|
||||
title: _t('Select a view'),
|
||||
disable_multiple_selection: true,
|
||||
domain: [['type', '!=', 'qweb'], ['type', '!=', 'search']],
|
||||
on_selected: function (records) {
|
||||
self._rpc({
|
||||
model: 'ir.ui.view',
|
||||
method: 'search_read',
|
||||
domain: [['id', '=', records[0].id]],
|
||||
fields: ['name', 'model', 'type'],
|
||||
limit: 1,
|
||||
})
|
||||
.then(function (views) {
|
||||
var view = views[0];
|
||||
view.type = view.type === 'tree' ? 'list' : view.type; // ignore tree view
|
||||
self.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
name: view.name,
|
||||
res_model: view.model,
|
||||
views: [[view.id, view.type]]
|
||||
});
|
||||
});
|
||||
}
|
||||
}).open();
|
||||
},
|
||||
/**
|
||||
* Runs the JS (desktop) tests
|
||||
*/
|
||||
perform_js_tests: function () {
|
||||
this.do_action({
|
||||
name: _t("JS Tests"),
|
||||
target: 'new',
|
||||
type: 'ir.actions.act_url',
|
||||
url: '/web/tests?mod=*'
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Runs the JS mobile tests
|
||||
*/
|
||||
perform_js_mobile_tests: function () {
|
||||
this.do_action({
|
||||
name: _t("JS Mobile Tests"),
|
||||
target: 'new',
|
||||
type: 'ir.actions.act_url',
|
||||
url: '/web/tests/mobile?mod=*'
|
||||
});
|
||||
},
|
||||
perform_click_everywhere_test: function () {
|
||||
var $homeMenu = $("nav.o_main_navbar > a.o_menu_toggle.fa-th");
|
||||
$homeMenu.click();
|
||||
startClickEverywhere();
|
||||
},
|
||||
split_assets: function() {
|
||||
split_assets: function () {
|
||||
window.location = $.param.querystring(window.location.href, 'debug=assets');
|
||||
},
|
||||
tests_assets: function () {
|
||||
// Enable also 'assets' to see non minimized assets
|
||||
window.location = $.param.querystring(window.location.href, 'debug=assets,tests');
|
||||
},
|
||||
/**
|
||||
* Delete assets bundles to force their regeneration
|
||||
*
|
||||
@@ -240,667 +101,15 @@ var DebugManager = Widget.extend({
|
||||
model: 'ir.attachment',
|
||||
method: 'unlink',
|
||||
args: [ids],
|
||||
}).then(self.do_action('reload'));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DebugManager features depending on having an action, and possibly a model
|
||||
* (window action)
|
||||
*/
|
||||
DebugManager.include({
|
||||
/**
|
||||
* Updates current action (action descriptor) on tag = action,
|
||||
*/
|
||||
update: function (tag, descriptor) {
|
||||
if (tag === 'action') {
|
||||
this._action = descriptor;
|
||||
}
|
||||
return this._super().then(function () {
|
||||
this.$dropdown.find(".o_debug_leave_section").before(QWeb.render('WebClient.DebugManager.Action', {
|
||||
manager: this,
|
||||
action: this._action
|
||||
}));
|
||||
}.bind(this));
|
||||
},
|
||||
edit: function (params, evt) {
|
||||
this.do_action({
|
||||
res_model: params.model,
|
||||
res_id: params.id,
|
||||
name: evt.target.text,
|
||||
type: 'ir.actions.act_window',
|
||||
views: [[false, 'form']],
|
||||
view_mode: 'form',
|
||||
target: 'new',
|
||||
flags: {action_buttons: true, headless: true}
|
||||
}).then(window.location.reload());
|
||||
});
|
||||
},
|
||||
get_view_fields: function () {
|
||||
var model = this._action.res_model,
|
||||
self = this;
|
||||
this._rpc({
|
||||
model: 'ir.model',
|
||||
method: 'search',
|
||||
args: [[['model', '=', model]]]
|
||||
}).then(function (ids) {
|
||||
self.do_action({
|
||||
res_model: 'ir.model.fields',
|
||||
name: _t('View Fields'),
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
domain: [['model_id', '=', model]],
|
||||
type: 'ir.actions.act_window',
|
||||
context: {
|
||||
'default_model_id': ids[0]
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
manage_filters: function () {
|
||||
this.do_action({
|
||||
res_model: 'ir.filters',
|
||||
name: _t('Manage Filters'),
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
type: 'ir.actions.act_window',
|
||||
context: {
|
||||
search_default_my_filters: true,
|
||||
search_default_model_id: this._action.res_model
|
||||
}
|
||||
});
|
||||
},
|
||||
translate: function() {
|
||||
this._rpc({
|
||||
model: 'ir.translation',
|
||||
method: 'get_technical_translations',
|
||||
args: [this._action.res_model],
|
||||
})
|
||||
.then(this.do_action);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DebugManager features depending on having a form view or single record.
|
||||
* These could theoretically be split, but for now they'll be considered one
|
||||
* and the same.
|
||||
*/
|
||||
DebugManager.include({
|
||||
start: function () {
|
||||
this._can_edit_views = false;
|
||||
return Promise.all([
|
||||
this._super(),
|
||||
this._rpc({
|
||||
model: 'ir.ui.view',
|
||||
method: 'check_access_rights',
|
||||
kwargs: {operation: 'write', raise_exception: false},
|
||||
})
|
||||
.then(function (ar) {
|
||||
this._can_edit_views = ar;
|
||||
}.bind(this))
|
||||
]
|
||||
);
|
||||
},
|
||||
update: function (tag, descriptor, widget) {
|
||||
if (tag === 'action' || tag === 'view') {
|
||||
this._controller = widget;
|
||||
}
|
||||
return this._super(tag, descriptor).then(function () {
|
||||
this.$dropdown.find(".o_debug_leave_section").before(QWeb.render('WebClient.DebugManager.View', {
|
||||
action: this._action,
|
||||
can_edit: this._can_edit_views,
|
||||
controller: this._controller,
|
||||
controlPanelView: this._controller && this._controller._controlPanel,
|
||||
manager: this,
|
||||
view: this._controller && _.findWhere(this._action.views, {
|
||||
type: this._controller.viewType,
|
||||
}),
|
||||
}));
|
||||
}.bind(this));
|
||||
},
|
||||
get_attachments: function() {
|
||||
var selectedIDs = this._controller.getSelectedIds();
|
||||
if (!selectedIDs.length) {
|
||||
console.warn(_t("No attachment available"));
|
||||
return;
|
||||
}
|
||||
this.do_action({
|
||||
res_model: 'ir.attachment',
|
||||
name: _t('Manage Attachments'),
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
type: 'ir.actions.act_window',
|
||||
domain: [['res_model', '=', this._action.res_model], ['res_id', '=', selectedIDs[0]]],
|
||||
context: {
|
||||
default_res_model: this._action.res_model,
|
||||
default_res_id: selectedIDs[0],
|
||||
},
|
||||
});
|
||||
},
|
||||
get_metadata: function() {
|
||||
var self = this;
|
||||
var selectedIDs = this._controller.getSelectedIds();
|
||||
if (!selectedIDs.length) {
|
||||
console.warn(_t("No metadata available"));
|
||||
return;
|
||||
}
|
||||
this._rpc({
|
||||
model: this._action.res_model,
|
||||
method: 'get_metadata',
|
||||
args: [selectedIDs],
|
||||
}).then(function(result) {
|
||||
var metadata = result[0];
|
||||
metadata.creator = field_utils.format.many2one(metadata.create_uid);
|
||||
metadata.lastModifiedBy = field_utils.format.many2one(metadata.write_uid);
|
||||
var createDate = field_utils.parse.datetime(metadata.create_date);
|
||||
metadata.create_date = field_utils.format.datetime(createDate);
|
||||
var modificationDate = field_utils.parse.datetime(metadata.write_date);
|
||||
metadata.write_date = field_utils.format.datetime(modificationDate);
|
||||
var dialog = new Dialog(this, {
|
||||
title: _.str.sprintf(_t("Metadata (%s)"), self._action.res_model),
|
||||
size: 'medium',
|
||||
$content: QWeb.render('WebClient.DebugViewLog', {
|
||||
perm : metadata,
|
||||
})
|
||||
});
|
||||
dialog.open().opened(function () {
|
||||
dialog.$el.on('click', 'a[data-action="toggle_noupdate"]', function (ev) {
|
||||
ev.preventDefault();
|
||||
self._rpc({
|
||||
model: 'ir.model.data',
|
||||
method: 'toggle_noupdate',
|
||||
args: [self._action.res_model, metadata.id]
|
||||
}).then(function (res) {
|
||||
dialog.close();
|
||||
self.get_metadata();
|
||||
})
|
||||
});
|
||||
})
|
||||
});
|
||||
},
|
||||
set_defaults: function() {
|
||||
var self = this;
|
||||
|
||||
var display = function (fieldInfo, value) {
|
||||
var displayed = value;
|
||||
if (value && fieldInfo.type === 'many2one') {
|
||||
displayed = value.data.display_name;
|
||||
value = value.data.id;
|
||||
} else if (value && fieldInfo.type === 'selection') {
|
||||
displayed = _.find(fieldInfo.selection, function (option) {
|
||||
return option[0] === value;
|
||||
})[1];
|
||||
}
|
||||
return [value, displayed];
|
||||
};
|
||||
|
||||
var renderer = this._controller.renderer;
|
||||
var state = renderer.state;
|
||||
var fields = state.fields;
|
||||
var fieldsInfo = state.fieldsInfo.form;
|
||||
var fieldNamesInView = state.getFieldNames();
|
||||
var fieldNamesOnlyOnView = ['message_attachment_count'];
|
||||
var fieldsValues = state.data;
|
||||
var modifierDatas = {};
|
||||
_.each(fieldNamesInView, function (fieldName) {
|
||||
modifierDatas[fieldName] = _.find(renderer.allModifiersData, function (modifierdata) {
|
||||
return modifierdata.node.attrs.name === fieldName;
|
||||
});
|
||||
});
|
||||
this.fields = _.chain(fieldNamesInView)
|
||||
.difference(fieldNamesOnlyOnView)
|
||||
.map(function (fieldName) {
|
||||
var modifierData = modifierDatas[fieldName];
|
||||
var invisibleOrReadOnly;
|
||||
if (modifierData) {
|
||||
var evaluatedModifiers = modifierData.evaluatedModifiers[state.id];
|
||||
invisibleOrReadOnly = evaluatedModifiers.invisible || evaluatedModifiers.readonly;
|
||||
}
|
||||
var fieldInfo = fields[fieldName];
|
||||
var valueDisplayed = display(fieldInfo, fieldsValues[fieldName]);
|
||||
var value = valueDisplayed[0];
|
||||
var displayed = valueDisplayed[1];
|
||||
// ignore fields which are empty, invisible, readonly, o2m
|
||||
// or m2m
|
||||
if (!value || invisibleOrReadOnly || fieldInfo.type === 'one2many' ||
|
||||
fieldInfo.type === 'many2many' || fieldInfo.type === 'binary' ||
|
||||
fieldsInfo[fieldName].options.isPassword || !_.isEmpty(fieldInfo.depends)) {
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
name: fieldName,
|
||||
string: fieldInfo.string,
|
||||
value: value,
|
||||
displayed: displayed,
|
||||
};
|
||||
})
|
||||
.compact()
|
||||
.sortBy(function (field) { return field.string; })
|
||||
.value();
|
||||
|
||||
var conditions = _.chain(fieldNamesInView)
|
||||
.filter(function (fieldName) {
|
||||
var fieldInfo = fields[fieldName];
|
||||
return fieldInfo.change_default;
|
||||
})
|
||||
.map(function (fieldName) {
|
||||
var fieldInfo = fields[fieldName];
|
||||
var valueDisplayed = display(fieldInfo, fieldsValues[fieldName]);
|
||||
var value = valueDisplayed[0];
|
||||
var displayed = valueDisplayed[1];
|
||||
return {
|
||||
name: fieldName,
|
||||
string: fieldInfo.string,
|
||||
value: value,
|
||||
displayed: displayed,
|
||||
};
|
||||
})
|
||||
.value();
|
||||
var d = new Dialog(this, {
|
||||
title: _t("Set Default"),
|
||||
buttons: [
|
||||
{text: _t("Close"), close: true},
|
||||
{text: _t("Save default"), click: function () {
|
||||
var $defaults = d.$el.find('#formview_default_fields');
|
||||
var fieldToSet = $defaults.val();
|
||||
if (!fieldToSet) {
|
||||
$defaults.parent().addClass('o_form_invalid');
|
||||
return;
|
||||
}
|
||||
var selfUser = d.$el.find('#formview_default_self').is(':checked');
|
||||
var condition = d.$el.find('#formview_default_conditions').val();
|
||||
var value = _.find(self.fields, function (field) {
|
||||
return field.name === fieldToSet;
|
||||
}).value;
|
||||
self._rpc({
|
||||
model: 'ir.default',
|
||||
method: 'set',
|
||||
args: [
|
||||
self._action.res_model,
|
||||
fieldToSet,
|
||||
value,
|
||||
selfUser,
|
||||
true,
|
||||
condition || false,
|
||||
],
|
||||
}).then(function () { d.close(); });
|
||||
}}
|
||||
]
|
||||
});
|
||||
d.args = {
|
||||
fields: this.fields,
|
||||
conditions: conditions,
|
||||
};
|
||||
d.template = 'FormView.set_default';
|
||||
d.open();
|
||||
},
|
||||
fvg: function() {
|
||||
var self = this;
|
||||
var dialog = new Dialog(this, { title: _t("Fields View Get") });
|
||||
dialog.opened().then(function () {
|
||||
$('<pre>').text(utils.json_node_to_xml(
|
||||
self._controller.renderer.arch, true)
|
||||
).appendTo(dialog.$el);
|
||||
});
|
||||
dialog.open();
|
||||
leave_debug_mode: function () {
|
||||
var qs = $.deparam.querystring();
|
||||
qs.debug = '';
|
||||
window.location.search = '?' + $.param(qs);
|
||||
},
|
||||
});
|
||||
function make_context(width, height, fn) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
// make e.layerX/e.layerY imitate e.offsetX/e.offsetY.
|
||||
canvas.style.position = 'relative';
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
ctx.mozImageSmoothingEnabled = false;
|
||||
ctx.oImageSmoothingEnabled = false;
|
||||
ctx.webkitImageSmoothingEnabled = false;
|
||||
fn && fn(ctx);
|
||||
return ctx;
|
||||
}
|
||||
var RequestsOverlay = Widget.extend({
|
||||
template: 'WebClient.DebugManager.RequestsOverlay',
|
||||
TRACKS: 8,
|
||||
TRACK_WIDTH: 9,
|
||||
events: {
|
||||
mousemove: function (e) {
|
||||
this.$tooltip.hide();
|
||||
}
|
||||
},
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._render = _.throttle(
|
||||
this._render.bind(this),
|
||||
1000/15, {leading: false}
|
||||
);
|
||||
},
|
||||
start: function () {
|
||||
var _super = this._super();
|
||||
this.$tooltip = this.$('div.o_debug_tooltip');
|
||||
this.getParent().on('update-stats', this, this._render);
|
||||
this._render();
|
||||
return _super;
|
||||
},
|
||||
tooltip: function (text, start, end, x, y) {
|
||||
// x and y are hit point with respect to the viewport. To know where
|
||||
// this hit point is with respect to the overlay, subtract the offset
|
||||
// between viewport and overlay, then add scroll factor of overlay
|
||||
// (which isn't taken in account by the viewport).
|
||||
//
|
||||
// Normally the viewport overlay should sum offsets of all
|
||||
// offsetParents until we reach `null` but in this case the overlay
|
||||
// should have been added directly to the body, which should have an
|
||||
// offset of 0.
|
||||
|
||||
var top = y - this.el.offsetTop + this.el.scrollTop + 1;
|
||||
var left = x - this.el.offsetLeft + this.el.scrollLeft + 1;
|
||||
this.$tooltip.css({top: top, left: left}).show()[0].innerHTML = ['<p>', text, ' (', (end - start), 'ms)', '</p>'].join('');
|
||||
},
|
||||
|
||||
_render: function () {
|
||||
var $summary = this.$('header'),
|
||||
w = $summary[0].clientWidth,
|
||||
$requests = this.$('.o_debug_requests');
|
||||
$summary.find('canvas').attr('width', w);
|
||||
var tracks = document.getElementById('o_debug_requests_summary');
|
||||
|
||||
_.invoke(this.getChildren(), 'destroy');
|
||||
|
||||
var requests = this.getParent()._events;
|
||||
var bounds = this._get_bounds(requests);
|
||||
// horizontal scaling factor for summary
|
||||
var scale = w / (bounds.high - bounds.low);
|
||||
|
||||
// store end-time of "current" requests, to find out which track a
|
||||
// request should go in, just look for the first track whose end-time
|
||||
// is smaller than the new request's start time.
|
||||
var track_ends = _(this.TRACKS).times(_.constant(-Infinity));
|
||||
|
||||
var ctx = tracks.getContext('2d');
|
||||
ctx.lineWidth = this.TRACK_WIDTH;
|
||||
for (var i = 0; i < requests.length; i++) {
|
||||
var request = requests[i];
|
||||
// FIXME: is it certain that events in the request are sorted by timestamp?
|
||||
var rstart = Math.floor(request[0][3] * 1e3);
|
||||
var rend = Math.ceil(request[request.length - 1][3] * 1e3);
|
||||
// find free track for current request
|
||||
for(var track=0; track < track_ends.length; ++track) {
|
||||
if (track_ends[track] < rstart) { break; }
|
||||
}
|
||||
// FIXME: display error message of some sort? Re-render with larger area? Something?
|
||||
if (track >= track_ends.length) {
|
||||
console.warn("could not find an empty summary track");
|
||||
continue;
|
||||
}
|
||||
// set new track end
|
||||
track_ends[track] = rend;
|
||||
ctx.save();
|
||||
ctx.translate(Math.floor((rstart - bounds.low) * scale), track * (this.TRACK_WIDTH + 1));
|
||||
this._draw_request(request, ctx, 0, scale);
|
||||
ctx.restore();
|
||||
new RequestDetails(this, request, scale).appendTo($requests);
|
||||
}
|
||||
},
|
||||
_draw_request: function (request, to_context, step, hscale, handle_event) {
|
||||
// have one draw surface for each event type:
|
||||
// * no need to alter context from one event to the next, each surface
|
||||
// gets its own color for all its lifetime
|
||||
// * surfaces can be blended in a specified order, which means events
|
||||
// can be drawn in any order, no need to care about z-index while
|
||||
// serializing events to the surfaces
|
||||
var surfaces = {
|
||||
request: make_context(to_context.canvas.width, to_context.canvas.height, function (ctx) {
|
||||
ctx.strokeStyle = 'blue';
|
||||
ctx.fillStyle = '#88f';
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineWidth = 1;
|
||||
}),
|
||||
//func: make_context(to_context.canvas.width, to_context.canvas.height, function (ctx) {
|
||||
// ctx.strokeStyle = 'gray';
|
||||
// ctx.lineWidth = to_context.lineWidth;
|
||||
// ctx.translate(0, initial_offset);
|
||||
//}),
|
||||
sql: make_context(to_context.canvas.width, to_context.canvas.height, function (ctx) {
|
||||
ctx.strokeStyle = 'red';
|
||||
ctx.fillStyle = '#f88';
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineWidth = 1;
|
||||
}),
|
||||
template: make_context(to_context.canvas.width, to_context.canvas.height, function (ctx) {
|
||||
ctx.strokeStyle = 'green';
|
||||
ctx.fillStyle = '#8f8';
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineWidth = 1;
|
||||
})
|
||||
};
|
||||
// apply scaling manually so zooming in improves display precision
|
||||
var stacks = {}, start = Math.floor(request[0][3] * 1e3 * hscale);
|
||||
var event_idx = 0;
|
||||
|
||||
var rect_width = to_context.lineWidth;
|
||||
for (var i = 0; i < request.length; i++) {
|
||||
var type, m, event = request[i];
|
||||
var tag = event[0], timestamp = Math.floor(event[3] * 1e3 * hscale) - start;
|
||||
|
||||
if (m = /(\w+)-start/.exec(tag)) {
|
||||
type = m[1];
|
||||
if (!(type in stacks)) { stacks[type] = []; }
|
||||
handle_event && handle_event(event_idx, timestamp, event);
|
||||
stacks[type].push({
|
||||
timestamp: timestamp,
|
||||
idx: event_idx++
|
||||
});
|
||||
} else if (m = /(\w+)-end/.exec(tag)) {
|
||||
type = m[1];
|
||||
var stack = stacks[type];
|
||||
var estart = stack.pop(), duration = Math.ceil(timestamp - estart.timestamp);
|
||||
handle_event && handle_event(estart.idx, timestamp, event);
|
||||
|
||||
var surface = surfaces[type];
|
||||
if (!surface) { continue; } // FIXME: support for unknown event types
|
||||
|
||||
var y = step * estart.idx;
|
||||
// path rectangle for the current event on the relevant surface
|
||||
surface.rect(estart.timestamp + 0.5, y + 0.5, duration || 1, rect_width);
|
||||
}
|
||||
}
|
||||
// add each layer to the main canvas
|
||||
var keys = ['request', /*'func', */'template', 'sql'];
|
||||
for (var j = 0; j < keys.length; ++j) {
|
||||
// stroke and fill all rectangles for the relevant surface/context
|
||||
var ctx = surfaces[keys[j]];
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
to_context.drawImage(ctx.canvas, 0, 0);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Returns first and last events in milliseconds
|
||||
*
|
||||
* @param requests
|
||||
* @returns {{low: number, high: number}}
|
||||
* @private
|
||||
*/
|
||||
_get_bounds: function (requests) {
|
||||
var low = +Infinity;
|
||||
var high =-+Infinity;
|
||||
|
||||
for (var i = 0; i < requests.length; i++) {
|
||||
var request = requests[i];
|
||||
for (var j = 0; j < request.length; j++) {
|
||||
var event = request[j];
|
||||
var timestamp = event[3];
|
||||
low = Math.min(low, timestamp);
|
||||
high = Math.max(high, timestamp);
|
||||
}
|
||||
}
|
||||
return {low: Math.floor(low * 1e3), high: Math.ceil(high * 1e3)};
|
||||
}
|
||||
});
|
||||
var RequestDetails = Widget.extend({
|
||||
events: {
|
||||
click: function () {
|
||||
this._open = !this._open;
|
||||
this.render();
|
||||
},
|
||||
'mousemove canvas': function (e) {
|
||||
e.stopPropagation();
|
||||
var y = e.y || e.offsetY || e.layerY;
|
||||
if (!y) { return; }
|
||||
var event = this._payloads[Math.floor(y / this._REQ_HEIGHT)];
|
||||
if (!event) { return; }
|
||||
|
||||
this.getParent().tooltip(event.payload, event.start, event.stop, e.clientX, e.clientY);
|
||||
}
|
||||
},
|
||||
init: function (parent, request, scale) {
|
||||
this._super.apply(this, arguments);
|
||||
this._request = request;
|
||||
this._open = false;
|
||||
this._scale = scale;
|
||||
this._REQ_HEIGHT = 20;
|
||||
},
|
||||
start: function () {
|
||||
this.el.style.borderBottom = '1px solid black';
|
||||
this.render();
|
||||
return this._super();
|
||||
},
|
||||
render: function () {
|
||||
var request_cell_height = this._REQ_HEIGHT, TITLE_WIDTH = 200;
|
||||
var request = this._request;
|
||||
var req_start = request[0][3] * 1e3;
|
||||
var req_duration = request[request.length - 1][3] * 1e3 - req_start;
|
||||
var height = request_cell_height * (this._open ? request.length / 2 : 1);
|
||||
var cell_center = request_cell_height / 2;
|
||||
var ctx = make_context(210 + Math.ceil(req_duration * this._scale), height, function (ctx) {
|
||||
ctx.lineWidth = cell_center;
|
||||
});
|
||||
this.$el.empty().append(ctx.canvas);
|
||||
var payloads = this._payloads = [];
|
||||
|
||||
// lazy version: if the render is single-line (!this._open), the extra
|
||||
// content will be discarded when the text canvas gets pasted onto the
|
||||
// main canvas. An improvement would be to not do text rendering
|
||||
// beyond the first event for "closed" requests events… then again
|
||||
// that makes for more regular rendering profile?
|
||||
var text_ctx = make_context(TITLE_WIDTH, height, function (ctx) {
|
||||
ctx.font = '12px sans-serif';
|
||||
ctx.textAlign = 'right';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.translate(0, cell_center);
|
||||
});
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(TITLE_WIDTH + 10, ((request_cell_height/4)|0));
|
||||
|
||||
this.getParent()._draw_request(request, ctx, this._open ? request_cell_height : 0, this._scale, function (idx, timestamp, event) {
|
||||
if (/-start$/g.test(event[0])) {
|
||||
payloads.push({
|
||||
payload: event[2],
|
||||
start: timestamp,
|
||||
stop: null
|
||||
});
|
||||
|
||||
// we want ~200px wide, assume the average character is at
|
||||
// least 4px wide => there can be *at most* 49 characters
|
||||
var title = event[2];
|
||||
title = title.replace(/\s+$/, '');
|
||||
title = title.length <= 50 ? title : ('…' + title.slice(-49));
|
||||
while (text_ctx.measureText(title).width > 200) {
|
||||
title = '…' + title.slice(2);
|
||||
}
|
||||
text_ctx.fillText(title, TITLE_WIDTH, request_cell_height * idx);
|
||||
} else if (/-end$/g.test(event[0])) {
|
||||
payloads[idx].stop = timestamp;
|
||||
}
|
||||
});
|
||||
ctx.restore();
|
||||
// add the text layer to the main canvas
|
||||
ctx.drawImage(text_ctx.canvas, 0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
if (config.debug) {
|
||||
SystrayMenu.Items.push(DebugManager);
|
||||
|
||||
WebClient.include({
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
current_action_updated: function (action, controller) {
|
||||
this._super.apply(this, arguments);
|
||||
var debugManager = _.find(this.menu.systray_menu.widgets, function(item) {
|
||||
return item instanceof DebugManager;
|
||||
});
|
||||
debugManager.update('action', action, controller && controller.widget);
|
||||
},
|
||||
});
|
||||
|
||||
ActionManager.include({
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the action of the controller currently opened in a dialog,
|
||||
* i.e. a target='new' action, if any.
|
||||
*
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
getCurrentActionInDialog: function () {
|
||||
if (this.currentDialogController) {
|
||||
return this.actions[this.currentDialogController.actionID];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
* Returns the controller currently opened in a dialog, if any.
|
||||
*
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
getCurrentControllerInDialog: function () {
|
||||
return this.currentDialogController;
|
||||
},
|
||||
});
|
||||
|
||||
Dialog.include({
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
open: function() {
|
||||
var self = this;
|
||||
// if the dialog is opened by the ActionManager, instantiate a
|
||||
// DebugManager and insert it into the DOM once the dialog is opened
|
||||
// (delay this with a setTimeout(0) to ensure that the internal
|
||||
// state, i.e. the current action and controller, of the
|
||||
// ActionManager is set to properly update the DebugManager)
|
||||
this.opened(function() {
|
||||
setTimeout(function () {
|
||||
var parent = self.getParent();
|
||||
if (parent instanceof ActionManager) {
|
||||
var action = parent.getCurrentActionInDialog();
|
||||
if (action) {
|
||||
var controller = parent.getCurrentControllerInDialog();
|
||||
self.debugManager = new DebugManager(self);
|
||||
var $header = self.$modal.find('.modal-header:first');
|
||||
return self.debugManager.prependTo($header).then(function () {
|
||||
self.debugManager.update('action', action, controller.widget);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return DebugManager;
|
||||
|
||||
|
||||
@@ -0,0 +1,766 @@
|
||||
odoo.define('web.DebugManager.Backend', function (require) {
|
||||
"use strict";
|
||||
|
||||
var ActionManager = require('web.ActionManager');
|
||||
var DebugManager = require('web.DebugManager');
|
||||
var dialogs = require('web.view_dialogs');
|
||||
var startClickEverywhere = require('web.clickEverywhere');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var Dialog = require('web.Dialog');
|
||||
var SystrayMenu = require('web.SystrayMenu');
|
||||
var utils = require('web.utils');
|
||||
var WebClient = require('web.WebClient');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
var _t = core._t;
|
||||
|
||||
/**
|
||||
* DebugManager features depending on backend
|
||||
*/
|
||||
DebugManager.include({
|
||||
requests_clear: function () {
|
||||
if (!this._events) {
|
||||
return;
|
||||
}
|
||||
this._events = [];
|
||||
this.trigger('update-stats', this._events);
|
||||
},
|
||||
show_timelines: function () {
|
||||
if (this._overlay) {
|
||||
this._overlay.destroy();
|
||||
this._overlay = null;
|
||||
return;
|
||||
}
|
||||
this._overlay = new RequestsOverlay(this);
|
||||
this._overlay.appendTo(document.body);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates current action (action descriptor) on tag = action,
|
||||
*/
|
||||
update: function (tag, descriptor) {
|
||||
return this._super().then(function () {
|
||||
this.$dropdown.find(".o_debug_split_assets").before(QWeb.render('WebClient.DebugManager.Backend', {
|
||||
manager: this,
|
||||
}));
|
||||
}.bind(this));
|
||||
},
|
||||
select_view: function () {
|
||||
var self = this;
|
||||
new dialogs.SelectCreateDialog(this, {
|
||||
res_model: 'ir.ui.view',
|
||||
title: _t('Select a view'),
|
||||
disable_multiple_selection: true,
|
||||
domain: [['type', '!=', 'qweb'], ['type', '!=', 'search']],
|
||||
on_selected: function (records) {
|
||||
self._rpc({
|
||||
model: 'ir.ui.view',
|
||||
method: 'search_read',
|
||||
domain: [['id', '=', records[0].id]],
|
||||
fields: ['name', 'model', 'type'],
|
||||
limit: 1,
|
||||
})
|
||||
.then(function (views) {
|
||||
var view = views[0];
|
||||
view.type = view.type === 'tree' ? 'list' : view.type; // ignore tree view
|
||||
self.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
name: view.name,
|
||||
res_model: view.model,
|
||||
views: [[view.id, view.type]]
|
||||
});
|
||||
});
|
||||
}
|
||||
}).open();
|
||||
},
|
||||
/**
|
||||
* Runs the JS (desktop) tests
|
||||
*/
|
||||
perform_js_tests: function () {
|
||||
this.do_action({
|
||||
name: _t("JS Tests"),
|
||||
target: 'new',
|
||||
type: 'ir.actions.act_url',
|
||||
url: '/web/tests?mod=*'
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Runs the JS mobile tests
|
||||
*/
|
||||
perform_js_mobile_tests: function () {
|
||||
this.do_action({
|
||||
name: _t("JS Mobile Tests"),
|
||||
target: 'new',
|
||||
type: 'ir.actions.act_url',
|
||||
url: '/web/tests/mobile?mod=*'
|
||||
});
|
||||
},
|
||||
perform_click_everywhere_test: function () {
|
||||
var $homeMenu = $("nav.o_main_navbar > a.o_menu_toggle.fa-th");
|
||||
$homeMenu.click();
|
||||
startClickEverywhere();
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* DebugManager features depending on having an action, and possibly a model
|
||||
* (window action)
|
||||
*/
|
||||
DebugManager.include({
|
||||
/**
|
||||
* Updates current action (action descriptor) on tag = action,
|
||||
*/
|
||||
update: function (tag, descriptor) {
|
||||
if (tag === 'action') {
|
||||
this._action = descriptor;
|
||||
}
|
||||
return this._super().then(function () {
|
||||
this.$dropdown.find(".o_debug_leave_section").before(QWeb.render('WebClient.DebugManager.Action', {
|
||||
manager: this,
|
||||
action: this._action
|
||||
}));
|
||||
}.bind(this));
|
||||
},
|
||||
edit: function (params, evt) {
|
||||
this.do_action({
|
||||
res_model: params.model,
|
||||
res_id: params.id,
|
||||
name: evt.target.text,
|
||||
type: 'ir.actions.act_window',
|
||||
views: [[false, 'form']],
|
||||
view_mode: 'form',
|
||||
target: 'new',
|
||||
flags: {action_buttons: true, headless: true}
|
||||
});
|
||||
},
|
||||
get_view_fields: function () {
|
||||
var model = this._action.res_model,
|
||||
self = this;
|
||||
this._rpc({
|
||||
model: 'ir.model',
|
||||
method: 'search',
|
||||
args: [[['model', '=', model]]]
|
||||
}).then(function (ids) {
|
||||
self.do_action({
|
||||
res_model: 'ir.model.fields',
|
||||
name: _t('View Fields'),
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
domain: [['model_id', '=', model]],
|
||||
type: 'ir.actions.act_window',
|
||||
context: {
|
||||
'default_model_id': ids[0]
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
manage_filters: function () {
|
||||
this.do_action({
|
||||
res_model: 'ir.filters',
|
||||
name: _t('Manage Filters'),
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
type: 'ir.actions.act_window',
|
||||
context: {
|
||||
search_default_my_filters: true,
|
||||
search_default_model_id: this._action.res_model
|
||||
}
|
||||
});
|
||||
},
|
||||
translate: function() {
|
||||
this._rpc({
|
||||
model: 'ir.translation',
|
||||
method: 'get_technical_translations',
|
||||
args: [this._action.res_model],
|
||||
})
|
||||
.then(this.do_action);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DebugManager features depending on having a form view or single record.
|
||||
* These could theoretically be split, but for now they'll be considered one
|
||||
* and the same.
|
||||
*/
|
||||
DebugManager.include({
|
||||
start: function () {
|
||||
this._can_edit_views = false;
|
||||
return Promise.all([
|
||||
this._super(),
|
||||
this._rpc({
|
||||
model: 'ir.ui.view',
|
||||
method: 'check_access_rights',
|
||||
kwargs: {operation: 'write', raise_exception: false},
|
||||
})
|
||||
.then(function (ar) {
|
||||
this._can_edit_views = ar;
|
||||
}.bind(this))
|
||||
]
|
||||
);
|
||||
},
|
||||
update: function (tag, descriptor, widget) {
|
||||
if (tag === 'action' || tag === 'view') {
|
||||
this._controller = widget;
|
||||
}
|
||||
return this._super(tag, descriptor).then(function () {
|
||||
this.$dropdown.find(".o_debug_leave_section").before(QWeb.render('WebClient.DebugManager.View', {
|
||||
action: this._action,
|
||||
can_edit: this._can_edit_views,
|
||||
controller: this._controller,
|
||||
controlPanelView: this._controller && this._controller._controlPanel,
|
||||
manager: this,
|
||||
view: this._controller && _.findWhere(this._action.views, {
|
||||
type: this._controller.viewType,
|
||||
}),
|
||||
}));
|
||||
}.bind(this));
|
||||
},
|
||||
get_attachments: function() {
|
||||
var selectedIDs = this._controller.getSelectedIds();
|
||||
if (!selectedIDs.length) {
|
||||
console.warn(_t("No attachment available"));
|
||||
return;
|
||||
}
|
||||
this.do_action({
|
||||
res_model: 'ir.attachment',
|
||||
name: _t('Manage Attachments'),
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
type: 'ir.actions.act_window',
|
||||
domain: [['res_model', '=', this._action.res_model], ['res_id', '=', selectedIDs[0]]],
|
||||
context: {
|
||||
default_res_model: this._action.res_model,
|
||||
default_res_id: selectedIDs[0],
|
||||
},
|
||||
});
|
||||
},
|
||||
get_metadata: function() {
|
||||
var self = this;
|
||||
var selectedIDs = this._controller.getSelectedIds();
|
||||
if (!selectedIDs.length) {
|
||||
console.warn(_t("No metadata available"));
|
||||
return;
|
||||
}
|
||||
this._rpc({
|
||||
model: this._action.res_model,
|
||||
method: 'get_metadata',
|
||||
args: [selectedIDs],
|
||||
}).then(function(result) {
|
||||
var metadata = result[0];
|
||||
metadata.creator = field_utils.format.many2one(metadata.create_uid);
|
||||
metadata.lastModifiedBy = field_utils.format.many2one(metadata.write_uid);
|
||||
var createDate = field_utils.parse.datetime(metadata.create_date);
|
||||
metadata.create_date = field_utils.format.datetime(createDate);
|
||||
var modificationDate = field_utils.parse.datetime(metadata.write_date);
|
||||
metadata.write_date = field_utils.format.datetime(modificationDate);
|
||||
var dialog = new Dialog(this, {
|
||||
title: _.str.sprintf(_t("Metadata (%s)"), self._action.res_model),
|
||||
size: 'medium',
|
||||
$content: QWeb.render('WebClient.DebugViewLog', {
|
||||
perm : metadata,
|
||||
})
|
||||
});
|
||||
dialog.open().opened(function () {
|
||||
dialog.$el.on('click', 'a[data-action="toggle_noupdate"]', function (ev) {
|
||||
ev.preventDefault();
|
||||
self._rpc({
|
||||
model: 'ir.model.data',
|
||||
method: 'toggle_noupdate',
|
||||
args: [self._action.res_model, metadata.id]
|
||||
}).then(function (res) {
|
||||
dialog.close();
|
||||
self.get_metadata();
|
||||
})
|
||||
});
|
||||
})
|
||||
});
|
||||
},
|
||||
set_defaults: function() {
|
||||
var self = this;
|
||||
|
||||
var display = function (fieldInfo, value) {
|
||||
var displayed = value;
|
||||
if (value && fieldInfo.type === 'many2one') {
|
||||
displayed = value.data.display_name;
|
||||
value = value.data.id;
|
||||
} else if (value && fieldInfo.type === 'selection') {
|
||||
displayed = _.find(fieldInfo.selection, function (option) {
|
||||
return option[0] === value;
|
||||
})[1];
|
||||
}
|
||||
return [value, displayed];
|
||||
};
|
||||
|
||||
var renderer = this._controller.renderer;
|
||||
var state = renderer.state;
|
||||
var fields = state.fields;
|
||||
var fieldsInfo = state.fieldsInfo.form;
|
||||
var fieldNamesInView = state.getFieldNames();
|
||||
var fieldNamesOnlyOnView = ['message_attachment_count'];
|
||||
var fieldsValues = state.data;
|
||||
var modifierDatas = {};
|
||||
_.each(fieldNamesInView, function (fieldName) {
|
||||
modifierDatas[fieldName] = _.find(renderer.allModifiersData, function (modifierdata) {
|
||||
return modifierdata.node.attrs.name === fieldName;
|
||||
});
|
||||
});
|
||||
this.fields = _.chain(fieldNamesInView)
|
||||
.difference(fieldNamesOnlyOnView)
|
||||
.map(function (fieldName) {
|
||||
var modifierData = modifierDatas[fieldName];
|
||||
var invisibleOrReadOnly;
|
||||
if (modifierData) {
|
||||
var evaluatedModifiers = modifierData.evaluatedModifiers[state.id];
|
||||
invisibleOrReadOnly = evaluatedModifiers.invisible || evaluatedModifiers.readonly;
|
||||
}
|
||||
var fieldInfo = fields[fieldName];
|
||||
var valueDisplayed = display(fieldInfo, fieldsValues[fieldName]);
|
||||
var value = valueDisplayed[0];
|
||||
var displayed = valueDisplayed[1];
|
||||
// ignore fields which are empty, invisible, readonly, o2m
|
||||
// or m2m
|
||||
if (!value || invisibleOrReadOnly || fieldInfo.type === 'one2many' ||
|
||||
fieldInfo.type === 'many2many' || fieldInfo.type === 'binary' ||
|
||||
fieldsInfo[fieldName].options.isPassword || !_.isEmpty(fieldInfo.depends)) {
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
name: fieldName,
|
||||
string: fieldInfo.string,
|
||||
value: value,
|
||||
displayed: displayed,
|
||||
};
|
||||
})
|
||||
.compact()
|
||||
.sortBy(function (field) { return field.string; })
|
||||
.value();
|
||||
|
||||
var conditions = _.chain(fieldNamesInView)
|
||||
.filter(function (fieldName) {
|
||||
var fieldInfo = fields[fieldName];
|
||||
return fieldInfo.change_default;
|
||||
})
|
||||
.map(function (fieldName) {
|
||||
var fieldInfo = fields[fieldName];
|
||||
var valueDisplayed = display(fieldInfo, fieldsValues[fieldName]);
|
||||
var value = valueDisplayed[0];
|
||||
var displayed = valueDisplayed[1];
|
||||
return {
|
||||
name: fieldName,
|
||||
string: fieldInfo.string,
|
||||
value: value,
|
||||
displayed: displayed,
|
||||
};
|
||||
})
|
||||
.value();
|
||||
var d = new Dialog(this, {
|
||||
title: _t("Set Default"),
|
||||
buttons: [
|
||||
{text: _t("Close"), close: true},
|
||||
{text: _t("Save default"), click: function () {
|
||||
var $defaults = d.$el.find('#formview_default_fields');
|
||||
var fieldToSet = $defaults.val();
|
||||
if (!fieldToSet) {
|
||||
$defaults.parent().addClass('o_form_invalid');
|
||||
return;
|
||||
}
|
||||
var selfUser = d.$el.find('#formview_default_self').is(':checked');
|
||||
var condition = d.$el.find('#formview_default_conditions').val();
|
||||
var value = _.find(self.fields, function (field) {
|
||||
return field.name === fieldToSet;
|
||||
}).value;
|
||||
self._rpc({
|
||||
model: 'ir.default',
|
||||
method: 'set',
|
||||
args: [
|
||||
self._action.res_model,
|
||||
fieldToSet,
|
||||
value,
|
||||
selfUser,
|
||||
true,
|
||||
condition || false,
|
||||
],
|
||||
}).then(function () { d.close(); });
|
||||
}}
|
||||
]
|
||||
});
|
||||
d.args = {
|
||||
fields: this.fields,
|
||||
conditions: conditions,
|
||||
};
|
||||
d.template = 'FormView.set_default';
|
||||
d.open();
|
||||
},
|
||||
fvg: function() {
|
||||
var self = this;
|
||||
var dialog = new Dialog(this, { title: _t("Fields View Get") });
|
||||
dialog.opened().then(function () {
|
||||
$('<pre>').text(utils.json_node_to_xml(
|
||||
self._controller.renderer.arch, true)
|
||||
).appendTo(dialog.$el);
|
||||
});
|
||||
dialog.open();
|
||||
},
|
||||
});
|
||||
function make_context(width, height, fn) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
// make e.layerX/e.layerY imitate e.offsetX/e.offsetY.
|
||||
canvas.style.position = 'relative';
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
ctx.mozImageSmoothingEnabled = false;
|
||||
ctx.oImageSmoothingEnabled = false;
|
||||
ctx.webkitImageSmoothingEnabled = false;
|
||||
fn && fn(ctx);
|
||||
return ctx;
|
||||
}
|
||||
var RequestsOverlay = Widget.extend({
|
||||
template: 'WebClient.DebugManager.RequestsOverlay',
|
||||
TRACKS: 8,
|
||||
TRACK_WIDTH: 9,
|
||||
events: {
|
||||
mousemove: function (e) {
|
||||
this.$tooltip.hide();
|
||||
}
|
||||
},
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._render = _.throttle(
|
||||
this._render.bind(this),
|
||||
1000/15, {leading: false}
|
||||
);
|
||||
},
|
||||
start: function () {
|
||||
var _super = this._super();
|
||||
this.$tooltip = this.$('div.o_debug_tooltip');
|
||||
this.getParent().on('update-stats', this, this._render);
|
||||
this._render();
|
||||
return _super;
|
||||
},
|
||||
tooltip: function (text, start, end, x, y) {
|
||||
// x and y are hit point with respect to the viewport. To know where
|
||||
// this hit point is with respect to the overlay, subtract the offset
|
||||
// between viewport and overlay, then add scroll factor of overlay
|
||||
// (which isn't taken in account by the viewport).
|
||||
//
|
||||
// Normally the viewport overlay should sum offsets of all
|
||||
// offsetParents until we reach `null` but in this case the overlay
|
||||
// should have been added directly to the body, which should have an
|
||||
// offset of 0.
|
||||
|
||||
var top = y - this.el.offsetTop + this.el.scrollTop + 1;
|
||||
var left = x - this.el.offsetLeft + this.el.scrollLeft + 1;
|
||||
this.$tooltip.css({top: top, left: left}).show()[0].innerHTML = ['<p>', text, ' (', (end - start), 'ms)', '</p>'].join('');
|
||||
},
|
||||
|
||||
_render: function () {
|
||||
var $summary = this.$('header'),
|
||||
w = $summary[0].clientWidth,
|
||||
$requests = this.$('.o_debug_requests');
|
||||
$summary.find('canvas').attr('width', w);
|
||||
var tracks = document.getElementById('o_debug_requests_summary');
|
||||
|
||||
_.invoke(this.getChildren(), 'destroy');
|
||||
|
||||
var requests = this.getParent()._events;
|
||||
var bounds = this._get_bounds(requests);
|
||||
// horizontal scaling factor for summary
|
||||
var scale = w / (bounds.high - bounds.low);
|
||||
|
||||
// store end-time of "current" requests, to find out which track a
|
||||
// request should go in, just look for the first track whose end-time
|
||||
// is smaller than the new request's start time.
|
||||
var track_ends = _(this.TRACKS).times(_.constant(-Infinity));
|
||||
|
||||
var ctx = tracks.getContext('2d');
|
||||
ctx.lineWidth = this.TRACK_WIDTH;
|
||||
for (var i = 0; i < requests.length; i++) {
|
||||
var request = requests[i];
|
||||
// FIXME: is it certain that events in the request are sorted by timestamp?
|
||||
var rstart = Math.floor(request[0][3] * 1e3);
|
||||
var rend = Math.ceil(request[request.length - 1][3] * 1e3);
|
||||
// find free track for current request
|
||||
for(var track=0; track < track_ends.length; ++track) {
|
||||
if (track_ends[track] < rstart) { break; }
|
||||
}
|
||||
// FIXME: display error message of some sort? Re-render with larger area? Something?
|
||||
if (track >= track_ends.length) {
|
||||
console.warn("could not find an empty summary track");
|
||||
continue;
|
||||
}
|
||||
// set new track end
|
||||
track_ends[track] = rend;
|
||||
ctx.save();
|
||||
ctx.translate(Math.floor((rstart - bounds.low) * scale), track * (this.TRACK_WIDTH + 1));
|
||||
this._draw_request(request, ctx, 0, scale);
|
||||
ctx.restore();
|
||||
new RequestDetails(this, request, scale).appendTo($requests);
|
||||
}
|
||||
},
|
||||
_draw_request: function (request, to_context, step, hscale, handle_event) {
|
||||
// have one draw surface for each event type:
|
||||
// * no need to alter context from one event to the next, each surface
|
||||
// gets its own color for all its lifetime
|
||||
// * surfaces can be blended in a specified order, which means events
|
||||
// can be drawn in any order, no need to care about z-index while
|
||||
// serializing events to the surfaces
|
||||
var surfaces = {
|
||||
request: make_context(to_context.canvas.width, to_context.canvas.height, function (ctx) {
|
||||
ctx.strokeStyle = 'blue';
|
||||
ctx.fillStyle = '#88f';
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineWidth = 1;
|
||||
}),
|
||||
//func: make_context(to_context.canvas.width, to_context.canvas.height, function (ctx) {
|
||||
// ctx.strokeStyle = 'gray';
|
||||
// ctx.lineWidth = to_context.lineWidth;
|
||||
// ctx.translate(0, initial_offset);
|
||||
//}),
|
||||
sql: make_context(to_context.canvas.width, to_context.canvas.height, function (ctx) {
|
||||
ctx.strokeStyle = 'red';
|
||||
ctx.fillStyle = '#f88';
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineWidth = 1;
|
||||
}),
|
||||
template: make_context(to_context.canvas.width, to_context.canvas.height, function (ctx) {
|
||||
ctx.strokeStyle = 'green';
|
||||
ctx.fillStyle = '#8f8';
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineWidth = 1;
|
||||
})
|
||||
};
|
||||
// apply scaling manually so zooming in improves display precision
|
||||
var stacks = {}, start = Math.floor(request[0][3] * 1e3 * hscale);
|
||||
var event_idx = 0;
|
||||
|
||||
var rect_width = to_context.lineWidth;
|
||||
for (var i = 0; i < request.length; i++) {
|
||||
var type, m, event = request[i];
|
||||
var tag = event[0], timestamp = Math.floor(event[3] * 1e3 * hscale) - start;
|
||||
|
||||
if (m = /(\w+)-start/.exec(tag)) {
|
||||
type = m[1];
|
||||
if (!(type in stacks)) { stacks[type] = []; }
|
||||
handle_event && handle_event(event_idx, timestamp, event);
|
||||
stacks[type].push({
|
||||
timestamp: timestamp,
|
||||
idx: event_idx++
|
||||
});
|
||||
} else if (m = /(\w+)-end/.exec(tag)) {
|
||||
type = m[1];
|
||||
var stack = stacks[type];
|
||||
var estart = stack.pop(), duration = Math.ceil(timestamp - estart.timestamp);
|
||||
handle_event && handle_event(estart.idx, timestamp, event);
|
||||
|
||||
var surface = surfaces[type];
|
||||
if (!surface) { continue; } // FIXME: support for unknown event types
|
||||
|
||||
var y = step * estart.idx;
|
||||
// path rectangle for the current event on the relevant surface
|
||||
surface.rect(estart.timestamp + 0.5, y + 0.5, duration || 1, rect_width);
|
||||
}
|
||||
}
|
||||
// add each layer to the main canvas
|
||||
var keys = ['request', /*'func', */'template', 'sql'];
|
||||
for (var j = 0; j < keys.length; ++j) {
|
||||
// stroke and fill all rectangles for the relevant surface/context
|
||||
var ctx = surfaces[keys[j]];
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
to_context.drawImage(ctx.canvas, 0, 0);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Returns first and last events in milliseconds
|
||||
*
|
||||
* @param requests
|
||||
* @returns {{low: number, high: number}}
|
||||
* @private
|
||||
*/
|
||||
_get_bounds: function (requests) {
|
||||
var low = +Infinity;
|
||||
var high =-+Infinity;
|
||||
|
||||
for (var i = 0; i < requests.length; i++) {
|
||||
var request = requests[i];
|
||||
for (var j = 0; j < request.length; j++) {
|
||||
var event = request[j];
|
||||
var timestamp = event[3];
|
||||
low = Math.min(low, timestamp);
|
||||
high = Math.max(high, timestamp);
|
||||
}
|
||||
}
|
||||
return {low: Math.floor(low * 1e3), high: Math.ceil(high * 1e3)};
|
||||
}
|
||||
});
|
||||
var RequestDetails = Widget.extend({
|
||||
events: {
|
||||
click: function () {
|
||||
this._open = !this._open;
|
||||
this.render();
|
||||
},
|
||||
'mousemove canvas': function (e) {
|
||||
e.stopPropagation();
|
||||
var y = e.y || e.offsetY || e.layerY;
|
||||
if (!y) { return; }
|
||||
var event = this._payloads[Math.floor(y / this._REQ_HEIGHT)];
|
||||
if (!event) { return; }
|
||||
|
||||
this.getParent().tooltip(event.payload, event.start, event.stop, e.clientX, e.clientY);
|
||||
}
|
||||
},
|
||||
init: function (parent, request, scale) {
|
||||
this._super.apply(this, arguments);
|
||||
this._request = request;
|
||||
this._open = false;
|
||||
this._scale = scale;
|
||||
this._REQ_HEIGHT = 20;
|
||||
},
|
||||
start: function () {
|
||||
this.el.style.borderBottom = '1px solid black';
|
||||
this.render();
|
||||
return this._super();
|
||||
},
|
||||
render: function () {
|
||||
var request_cell_height = this._REQ_HEIGHT, TITLE_WIDTH = 200;
|
||||
var request = this._request;
|
||||
var req_start = request[0][3] * 1e3;
|
||||
var req_duration = request[request.length - 1][3] * 1e3 - req_start;
|
||||
var height = request_cell_height * (this._open ? request.length / 2 : 1);
|
||||
var cell_center = request_cell_height / 2;
|
||||
var ctx = make_context(210 + Math.ceil(req_duration * this._scale), height, function (ctx) {
|
||||
ctx.lineWidth = cell_center;
|
||||
});
|
||||
this.$el.empty().append(ctx.canvas);
|
||||
var payloads = this._payloads = [];
|
||||
|
||||
// lazy version: if the render is single-line (!this._open), the extra
|
||||
// content will be discarded when the text canvas gets pasted onto the
|
||||
// main canvas. An improvement would be to not do text rendering
|
||||
// beyond the first event for "closed" requests events… then again
|
||||
// that makes for more regular rendering profile?
|
||||
var text_ctx = make_context(TITLE_WIDTH, height, function (ctx) {
|
||||
ctx.font = '12px sans-serif';
|
||||
ctx.textAlign = 'right';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.translate(0, cell_center);
|
||||
});
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(TITLE_WIDTH + 10, ((request_cell_height/4)|0));
|
||||
|
||||
this.getParent()._draw_request(request, ctx, this._open ? request_cell_height : 0, this._scale, function (idx, timestamp, event) {
|
||||
if (/-start$/g.test(event[0])) {
|
||||
payloads.push({
|
||||
payload: event[2],
|
||||
start: timestamp,
|
||||
stop: null
|
||||
});
|
||||
|
||||
// we want ~200px wide, assume the average character is at
|
||||
// least 4px wide => there can be *at most* 49 characters
|
||||
var title = event[2];
|
||||
title = title.replace(/\s+$/, '');
|
||||
title = title.length <= 50 ? title : ('…' + title.slice(-49));
|
||||
while (text_ctx.measureText(title).width > 200) {
|
||||
title = '…' + title.slice(2);
|
||||
}
|
||||
text_ctx.fillText(title, TITLE_WIDTH, request_cell_height * idx);
|
||||
} else if (/-end$/g.test(event[0])) {
|
||||
payloads[idx].stop = timestamp;
|
||||
}
|
||||
});
|
||||
ctx.restore();
|
||||
// add the text layer to the main canvas
|
||||
ctx.drawImage(text_ctx.canvas, 0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
if (config.isDebug()) {
|
||||
SystrayMenu.Items.push(DebugManager);
|
||||
|
||||
WebClient.include({
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
current_action_updated: function (action, controller) {
|
||||
this._super.apply(this, arguments);
|
||||
var debugManager = _.find(this.menu.systray_menu.widgets, function(item) {
|
||||
return item instanceof DebugManager;
|
||||
});
|
||||
debugManager.update('action', action, controller && controller.widget);
|
||||
},
|
||||
});
|
||||
|
||||
ActionManager.include({
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the action of the controller currently opened in a dialog,
|
||||
* i.e. a target='new' action, if any.
|
||||
*
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
getCurrentActionInDialog: function () {
|
||||
if (this.currentDialogController) {
|
||||
return this.actions[this.currentDialogController.actionID];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
* Returns the controller currently opened in a dialog, if any.
|
||||
*
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
getCurrentControllerInDialog: function () {
|
||||
return this.currentDialogController;
|
||||
},
|
||||
});
|
||||
|
||||
Dialog.include({
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
open: function() {
|
||||
var self = this;
|
||||
// if the dialog is opened by the ActionManager, instantiate a
|
||||
// DebugManager and insert it into the DOM once the dialog is opened
|
||||
// (delay this with a setTimeout(0) to ensure that the internal
|
||||
// state, i.e. the current action and controller, of the
|
||||
// ActionManager is set to properly update the DebugManager)
|
||||
this.opened(function() {
|
||||
setTimeout(function () {
|
||||
var parent = self.getParent();
|
||||
if (parent instanceof ActionManager) {
|
||||
var action = parent.getCurrentActionInDialog();
|
||||
if (action) {
|
||||
var controller = parent.getCurrentControllerInDialog();
|
||||
self.debugManager = new DebugManager(self);
|
||||
var $header = self.$modal.find('.modal-header:first');
|
||||
return self.debugManager.prependTo($header).then(function () {
|
||||
self.debugManager.update('action', action, controller.widget);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return DebugManager;
|
||||
|
||||
});
|
||||
@@ -235,7 +235,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
||||
$node.tooltip({
|
||||
title: function () {
|
||||
return qweb.render('WidgetLabel.tooltip', {
|
||||
debug: config.debug,
|
||||
debug: config.isDebug(),
|
||||
widget: widget,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ return AbstractRenderer.extend({
|
||||
this.color_map = {};
|
||||
|
||||
if (params.eventTemplate) {
|
||||
this.qweb = new QWeb(session.debug, {_s: session.origin});
|
||||
this.qweb = new QWeb(config.isDebug(), {_s: session.origin});
|
||||
this.qweb.add_template(utils.json_node_to_xml(params.eventTemplate));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -310,7 +310,7 @@ var FormRenderer = BasicRenderer.extend({
|
||||
$button.tooltip({
|
||||
title: function () {
|
||||
return qweb.render('WidgetButton.tooltip', {
|
||||
debug: config.debug,
|
||||
debug: config.isDebug(),
|
||||
state: self.state,
|
||||
node: node,
|
||||
});
|
||||
@@ -491,7 +491,7 @@ var FormRenderer = BasicRenderer.extend({
|
||||
this._registerModifiers(node, this.state, $button);
|
||||
|
||||
// Display tooltip
|
||||
if (config.debug || node.attrs.help) {
|
||||
if (config.isDebug() || node.attrs.help) {
|
||||
this._addButtonTooltip(node, $button);
|
||||
}
|
||||
return $button;
|
||||
@@ -727,7 +727,7 @@ var FormRenderer = BasicRenderer.extend({
|
||||
this._registerModifiers(node, this.state, $button);
|
||||
|
||||
// Display tooltip
|
||||
if (config.debug || node.attrs.help) {
|
||||
if (config.isDebug() || node.attrs.help) {
|
||||
this._addButtonTooltip(node, $button);
|
||||
}
|
||||
|
||||
@@ -998,7 +998,7 @@ var FormRenderer = BasicRenderer.extend({
|
||||
var $widgets = self.$('.o_field_widget[name=' + widget.name + ']');
|
||||
var $label = idForLabel ? self.$('.o_form_label[for=' + idForLabel + ']') : $();
|
||||
$label = $label.eq($widgets.index(widget.$el));
|
||||
if (config.debug || widget.attrs.help || widget.field.help) {
|
||||
if (config.isDebug() || widget.attrs.help || widget.field.help) {
|
||||
self._addFieldTooltip(widget, $label);
|
||||
}
|
||||
if (widget.attrs.widget === 'upgrade_boolean') {
|
||||
|
||||
@@ -245,7 +245,7 @@ var KanbanRecord = Widget.extend({
|
||||
if (Widget) {
|
||||
widget = self._processWidget($field, field_name, Widget);
|
||||
self.subWidgets[field_name] = widget;
|
||||
} else if (config.debug) {
|
||||
} else if (config.isDebug()) {
|
||||
// the widget is not implemented
|
||||
$field.replaceWith($('<span>', {
|
||||
text: _.str.sprintf(_t('[No widget %s]'), field_widget),
|
||||
|
||||
@@ -3,6 +3,7 @@ odoo.define('web.KanbanRenderer', function (require) {
|
||||
|
||||
var BasicRenderer = require('web.BasicRenderer');
|
||||
var ColumnQuickCreate = require('web.kanban_column_quick_create');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var KanbanColumn = require('web.KanbanColumn');
|
||||
var KanbanRecord = require('web.KanbanRecord');
|
||||
@@ -107,7 +108,7 @@ var KanbanRenderer = BasicRenderer.extend({
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.widgets = [];
|
||||
this.qweb = new QWeb(session.debug, {_s: session.origin}, false);
|
||||
this.qweb = new QWeb(config.isDebug(), {_s: session.origin}, false);
|
||||
var templates = findInNode(this.arch, function (n) { return n.tag === 'templates';});
|
||||
transformQwebTemplate(templates, state.fields);
|
||||
this.qweb.add_template(utils.json_node_to_xml(templates));
|
||||
|
||||
@@ -309,7 +309,7 @@ var ListRenderer = BasicRenderer.extend({
|
||||
|
||||
return _.map(this.columns, function (column) {
|
||||
var $cell = $('<td>');
|
||||
if (config.debug) {
|
||||
if (config.isDebug()) {
|
||||
$cell.addClass(column.attrs.name);
|
||||
}
|
||||
if (column.attrs.name in aggregateValues) {
|
||||
@@ -742,7 +742,7 @@ var ListRenderer = BasicRenderer.extend({
|
||||
$th.css({ textAlign: 'right' });
|
||||
}
|
||||
|
||||
if (config.debug) {
|
||||
if (config.isDebug()) {
|
||||
var fieldDescr = {
|
||||
field: field,
|
||||
name: name,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
odoo.define('web.DataExport', function (require) {
|
||||
"use strict";
|
||||
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var crash_manager = require('web.crash_manager');
|
||||
var Dialog = require('web.Dialog');
|
||||
@@ -240,11 +241,11 @@ var DataExport = Dialog.extend({
|
||||
.find('.o_expand_parent')
|
||||
.toggleClass('fa-chevron-right fa-chevron-down')
|
||||
.next()
|
||||
.after(QWeb.render('Export.TreeItems', {fields: records, debug: this.getSession().debug}));
|
||||
.after(QWeb.render('Export.TreeItems', {fields: records, debug: config.isDebug()}));
|
||||
} else {
|
||||
this.$('.o_left_field_panel').empty().append(
|
||||
$('<div/>').addClass('o_field_tree_structure')
|
||||
.append(QWeb.render('Export.TreeItems', {fields: records, debug: this.getSession().debug}))
|
||||
.append(QWeb.render('Export.TreeItems', {fields: records, debug: config.isDebug()}))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -218,109 +218,6 @@
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="WebClient.DebugManager">
|
||||
<li class="o_debug_manager" role="menuitem">
|
||||
<a role="button" href="#" title="Open Developer Tools" aria-label="Open Developer Tools" data-toggle="dropdown" aria-expanded="false" tabindex="-1" data-display="static">
|
||||
<span class="fa fa-bug"/>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right o_debug_dropdown" role="menu"/>
|
||||
</li>
|
||||
</t>
|
||||
<t t-name="WebClient.DebugManager.Global">
|
||||
<a role="menuitem" href="#" data-action="perform_js_tests" class="dropdown-item">Run JS Tests</a>
|
||||
<a role="menuitem" href="#" data-action="perform_js_mobile_tests" class="dropdown-item">Run JS Mobile Tests</a>
|
||||
<a role="menuitem" href="#" data-action="perform_click_everywhere_test" class="dropdown-item">Run Click Everywhere Test</a>
|
||||
<a role="menuitem" href="#" data-action="select_view" class="dropdown-item">Open View</a>
|
||||
<t t-if="manager._events">
|
||||
<div class="dropdown-divider" role="separator"/>
|
||||
<a role="menuitem" href="#" data-action="show_timelines" class="dropdown-item">Toggle Timelines</a>
|
||||
<a role="menuitem" href="#" data-action="requests_clear" class="dropdown-item">Clear Events</a>
|
||||
</t>
|
||||
<div class="dropdown-divider o_debug_leave_section" role="separator"/>
|
||||
<a role="menuitem" href="#" data-action="split_assets" class="dropdown-item">Activate Assets Debugging</a>
|
||||
<a role="menuitem" href="#" data-action="regenerateAssets" class="dropdown-item">Regenerate Assets Bundles</a>
|
||||
<a t-if="manager._is_admin" role="menuitem" href="/web/become" class="dropdown-item">Become Superuser</a>
|
||||
<a role="menuitem" href="#" data-action="leave_debug_mode" class="dropdown-item">Leave the Developer Tools</a>
|
||||
</t>
|
||||
<t t-name="WebClient.DebugManager.Action">
|
||||
<t t-if="action">
|
||||
<div class="dropdown-divider" role="separator"/>
|
||||
<a role="menuitem" href="#" data-action="edit" t-att-data-model="action.type" t-att-data-id="action.id" class="dropdown-item">Edit Action</a>
|
||||
<t t-if="action.res_model">
|
||||
<a role="menuitem" href="#" data-action="get_view_fields" class="dropdown-item">View Fields</a>
|
||||
<a role="menuitem" href="#" data-action="manage_filters" class="dropdown-item">Manage Filters</a>
|
||||
<a role="menuitem" href="#" data-action="translate" class="dropdown-item">Technical Translation</a>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="WebClient.DebugManager.View">
|
||||
<t t-if="view">
|
||||
<div role="separator" class="dropdown-divider"/>
|
||||
<t t-if="view.type === 'form'">
|
||||
<a role="menuitem" href="#" data-action="set_defaults" class="dropdown-item">Set Defaults</a>
|
||||
<t t-if="controller.getSelectedIds().length === 1">
|
||||
<a role="menuitem" href="#" data-action="get_metadata" class="dropdown-item">View Metadata</a>
|
||||
<a role="menuitem" href="#" data-action="get_attachments" class="dropdown-item">Manage Attachments</a>
|
||||
</t>
|
||||
</t>
|
||||
<a role="menuitem" href="#" data-action="fvg" class="dropdown-item">Fields View Get</a>
|
||||
<t t-if="can_edit">
|
||||
<a role="menuitem" href="#" data-action="edit" data-model="ir.ui.view" t-att-data-id="view.fieldsView.view_id" class="dropdown-item">
|
||||
Edit View: <t t-esc="view.displayName"/>
|
||||
</a>
|
||||
<a t-if="controlPanelView and controlPanelView.$el.is(':visible')" role="menuitem" href="#" data-action="edit" data-model="ir.ui.view" t-att-data-id="action.controlPanelFieldsView.view_id" class="dropdown-item">Edit ControlPanelView</a>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="WebClient.DebugViewLog">
|
||||
<table class="table table-sm table-striped">
|
||||
<tr>
|
||||
<th>ID:</th>
|
||||
<td><t t-esc="perm.id"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>XML ID:</th>
|
||||
<td><t t-esc="perm.xmlid or '/'"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>No Update:</th>
|
||||
<td>
|
||||
<t t-esc="perm.noupdate"/>
|
||||
<t t-if="perm.xmlid">
|
||||
<a data-action="toggle_noupdate">(change)</a>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creation User:</th>
|
||||
<td><t t-esc="perm.creator"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creation Date:</th>
|
||||
<td><t t-esc="perm.create_date"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Latest Modification by:</th>
|
||||
<td><t t-esc="perm.lastModifiedBy"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Latest Modification Date:</th>
|
||||
<td><t t-esc="perm.write_date"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
<div t-name="WebClient.DebugManager.RequestsOverlay" class="o_debug_manager_overlay">
|
||||
<header>
|
||||
<!-- enough height to display all tracks + 1px spacing between tracks -->
|
||||
<t t-set="canvas_height" t-value="widget.TRACKS * (widget.TRACK_WIDTH + 1)"/>
|
||||
<canvas t-att-height="canvas_height" id="o_debug_requests_summary"/>
|
||||
<!-- transparent overlay to display selected range -->
|
||||
<canvas t-att-height="canvas_height" id="o_debug_requests_selector"/>
|
||||
</header>
|
||||
<div class="o_debug_requests"/>
|
||||
<div class="o_debug_tooltip"/>
|
||||
</div>
|
||||
|
||||
<t t-name="Sidebar">
|
||||
<t t-foreach="widget.sections" t-as="section">
|
||||
<div class="btn-group o_dropdown">
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="WebClient.DebugManager">
|
||||
<li class="o_debug_manager" role="menuitem">
|
||||
<a role="button" href="#" class="o_debug_mode" t-attf-title="Open Developer Tools#{widget.debug_mode_help}"
|
||||
t-att-data-debug-mode="widget.debug_mode" aria-label="Open Developer Tools#{widget.debug_mode_help}"
|
||||
data-toggle="dropdown" aria-expanded="false" tabindex="-1" data-display="static">
|
||||
<span class="fa fa-bug"/>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right o_debug_dropdown" role="menu"/>
|
||||
</li>
|
||||
</t>
|
||||
<t t-name="WebClient.DebugManager.Global">
|
||||
<a role="menuitem" href="#" data-action="split_assets" class="dropdown-item o_debug_split_assets">Activate Assets Debugging</a>
|
||||
<a role="menuitem" href="#" data-action="tests_assets" class="dropdown-item">Activate Tests Assets Debugging</a>
|
||||
<a role="menuitem" href="#" data-action="regenerateAssets" class="dropdown-item">Regenerate Assets Bundles</a>
|
||||
<a t-if="manager._is_admin" role="menuitem" href="/web/become" class="dropdown-item">Become Superuser</a>
|
||||
<a role="menuitem" href="#" data-action="leave_debug_mode" class="dropdown-item">Leave the Developer Tools</a>
|
||||
</t>
|
||||
<t t-name="WebClient.DebugManager.Backend">
|
||||
<a role="menuitem" href="#" data-action="perform_js_tests" class="dropdown-item">Run JS Tests</a>
|
||||
<a role="menuitem" href="#" data-action="perform_js_mobile_tests" class="dropdown-item">Run JS Mobile Tests</a>
|
||||
<a role="menuitem" href="#" data-action="perform_click_everywhere_test" class="dropdown-item">Run Click Everywhere Test</a>
|
||||
<a role="menuitem" href="#" data-action="select_view" class="dropdown-item">Open View</a>
|
||||
<t t-if="manager._events">
|
||||
<div class="dropdown-divider" role="separator"/>
|
||||
<a role="menuitem" href="#" data-action="show_timelines" class="dropdown-item">Toggle Timelines</a>
|
||||
<a role="menuitem" href="#" data-action="requests_clear" class="dropdown-item">Clear Events</a>
|
||||
</t>
|
||||
<div class="dropdown-divider o_debug_leave_section" role="separator"/>
|
||||
</t>
|
||||
<t t-name="WebClient.DebugManager.Action">
|
||||
<t t-if="action">
|
||||
<div class="dropdown-divider" role="separator"/>
|
||||
<a role="menuitem" href="#" data-action="edit" t-att-data-model="action.type" t-att-data-id="action.id" class="dropdown-item">Edit Action</a>
|
||||
<t t-if="action.res_model">
|
||||
<a role="menuitem" href="#" data-action="get_view_fields" class="dropdown-item">View Fields</a>
|
||||
<a role="menuitem" href="#" data-action="manage_filters" class="dropdown-item">Manage Filters</a>
|
||||
<a role="menuitem" href="#" data-action="translate" class="dropdown-item">Technical Translation</a>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="WebClient.DebugManager.View">
|
||||
<t t-if="view">
|
||||
<div role="separator" class="dropdown-divider"/>
|
||||
<t t-if="view.type === 'form'">
|
||||
<a role="menuitem" href="#" data-action="set_defaults" class="dropdown-item">Set Defaults</a>
|
||||
<t t-if="controller.getSelectedIds().length === 1">
|
||||
<a role="menuitem" href="#" data-action="get_metadata" class="dropdown-item">View Metadata</a>
|
||||
<a role="menuitem" href="#" data-action="get_attachments" class="dropdown-item">Manage Attachments</a>
|
||||
</t>
|
||||
</t>
|
||||
<a role="menuitem" href="#" data-action="fvg" class="dropdown-item">Fields View Get</a>
|
||||
<t t-if="can_edit">
|
||||
<a role="menuitem" href="#" data-action="edit" data-model="ir.ui.view" t-att-data-id="view.fieldsView.view_id" class="dropdown-item">
|
||||
Edit View: <t t-esc="view.displayName"/>
|
||||
</a>
|
||||
<a t-if="controlPanelView and controlPanelView.$el.is(':visible')" role="menuitem" href="#" data-action="edit" data-model="ir.ui.view" t-att-data-id="action.controlPanelFieldsView.view_id" class="dropdown-item">Edit ControlPanelView</a>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="WebClient.DebugViewLog">
|
||||
<table class="table table-sm table-striped">
|
||||
<tr>
|
||||
<th>ID:</th>
|
||||
<td><t t-esc="perm.id"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>XML ID:</th>
|
||||
<td><t t-esc="perm.xmlid or '/'"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>No Update:</th>
|
||||
<td>
|
||||
<t t-esc="perm.noupdate"/>
|
||||
<t t-if="perm.xmlid">
|
||||
<a data-action="toggle_noupdate">(change)</a>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creation User:</th>
|
||||
<td><t t-esc="perm.creator"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creation Date:</th>
|
||||
<td><t t-esc="perm.create_date"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Latest Modification by:</th>
|
||||
<td><t t-esc="perm.lastModifiedBy"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Latest Modification Date:</th>
|
||||
<td><t t-esc="perm.write_date"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
<div t-name="WebClient.DebugManager.RequestsOverlay" class="o_debug_manager_overlay">
|
||||
<header>
|
||||
<!-- enough height to display all tracks + 1px spacing between tracks -->
|
||||
<t t-set="canvas_height" t-value="widget.TRACKS * (widget.TRACK_WIDTH + 1)"/>
|
||||
<canvas t-att-height="canvas_height" id="o_debug_requests_summary"/>
|
||||
<!-- transparent overlay to display selected range -->
|
||||
<canvas t-att-height="canvas_height" id="o_debug_requests_selector"/>
|
||||
</header>
|
||||
<div class="o_debug_requests"/>
|
||||
<div class="o_debug_tooltip"/>
|
||||
</div>
|
||||
|
||||
</templates>
|
||||
@@ -14,7 +14,7 @@ var ActionManager = require('web.ActionManager');
|
||||
var config = require('web.config');
|
||||
var ControlPanelView = require('web.ControlPanelView');
|
||||
var concurrency = require('web.concurrency');
|
||||
var DebugManager = require('web.DebugManager');
|
||||
var DebugManager = require('web.DebugManager.Backend');
|
||||
var dom = require('web.dom');
|
||||
var testUtilsMock = require('web.test_utils_mock');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
@@ -173,7 +173,7 @@ function addMockEnvironment(widget, params) {
|
||||
_.extend(config.device, params.config.device);
|
||||
}
|
||||
if ('debug' in params.config) {
|
||||
config.debug = params.config.debug;
|
||||
odoo.debug = params.config.debug;
|
||||
}
|
||||
}
|
||||
if ('translateParameters' in params) {
|
||||
|
||||
@@ -3,7 +3,6 @@ odoo.define('web.form_tests', function (require) {
|
||||
|
||||
var BasicModel = require('web.BasicModel');
|
||||
var concurrency = require('web.concurrency');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var fieldRegistry = require('web.field_registry');
|
||||
var FormView = require('web.FormView');
|
||||
@@ -6576,8 +6575,8 @@ QUnit.module('Views', {
|
||||
QUnit.test('display tooltips for buttons', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var initialDebugMode = config.debug;
|
||||
config.debug = true;
|
||||
var initialDebugMode = odoo.debug;
|
||||
odoo.debug = true;
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
@@ -6607,7 +6606,7 @@ QUnit.module('Views', {
|
||||
"should have rendered a tooltip");
|
||||
$secondButton.trigger($.Event('mouseleave'));
|
||||
|
||||
config.debug = initialDebugMode;
|
||||
odoo.debug = initialDebugMode;
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
@@ -6936,8 +6935,8 @@ QUnit.module('Views', {
|
||||
QUnit.test('proper stringification in debug mode tooltip', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
var initialDebugMode = config.debug;
|
||||
config.debug = true;
|
||||
var initialDebugMode = odoo.debug;
|
||||
odoo.debug = true;
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
@@ -6970,7 +6969,7 @@ QUnit.module('Views', {
|
||||
assert.strictEqual($('.oe_tooltip_technical>li[data-item="widget"]')[0].lastChild.wholeText.trim(),
|
||||
'Many2one (many2one)', "widget description should be correct");
|
||||
|
||||
config.debug = initialDebugMode;
|
||||
odoo.debug = initialDebugMode;
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ odoo.define('web.list_tests', function (require) {
|
||||
|
||||
var AbstractStorageService = require('web.AbstractStorageService');
|
||||
var BasicModel = require('web.BasicModel');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var basicFields = require('web.basic_fields');
|
||||
var FormView = require('web.FormView');
|
||||
@@ -1793,8 +1792,8 @@ QUnit.module('Views', {
|
||||
QUnit.test('display a tooltip on a field', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
var initialDebugMode = config.debug;
|
||||
config.debug = false;
|
||||
var initialDebugMode = odoo.debug;
|
||||
odoo.debug = false;
|
||||
|
||||
var list = await createView({
|
||||
View: ListView,
|
||||
@@ -1813,7 +1812,7 @@ QUnit.module('Views', {
|
||||
list.$('th[data-name=foo]').trigger($.Event('mouseenter'));
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_string').length, 0, "should not have rendered a tooltip");
|
||||
|
||||
config.debug = true;
|
||||
odoo.debug = true;
|
||||
// it is necessary to rerender the list so tooltips can be properly created
|
||||
await list.reload();
|
||||
list.$('th[data-name=foo]').tooltip('show', false);
|
||||
@@ -1828,7 +1827,7 @@ QUnit.module('Views', {
|
||||
assert.strictEqual($('.oe_tooltip_technical>li[data-item="widget"]')[0].lastChild.wholeText.trim(),
|
||||
'Button (toggle_button)', "widget description should be correct");
|
||||
|
||||
config.debug = initialDebugMode;
|
||||
odoo.debug = initialDebugMode;
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
|
||||
@@ -154,6 +154,8 @@
|
||||
<script type="text/javascript" src="/web/static/src/js/services/notification_service.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/services/session_storage_service.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/src/js/tools/debug_manager.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/name_and_signature.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/notification.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/rainbow_man.js"></script>
|
||||
@@ -248,8 +250,8 @@
|
||||
<script type="text/javascript" src="/web/static/src/js/services/session.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/auto_complete.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/change_password.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/tools/debug_manager.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/tools/test_menus_loader.js"/>
|
||||
<script type="text/javascript" src="/web/static/src/js/tools/debug_manager_backend.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/tools/tools.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/data_export.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/date_picker.js"></script>
|
||||
@@ -387,6 +389,14 @@
|
||||
<xpath expr="//t[@t-call='web._assets_frontend_minimal_js']" position="replace"/>
|
||||
</template>
|
||||
|
||||
<!-- Do not direcly call this template, call 'conditional_assets_tests' instead -->
|
||||
<template id="assets_tests" name="Tests Assets">
|
||||
</template>
|
||||
|
||||
<template id="conditional_assets_tests" name="Tests Assets Bundle">
|
||||
<t t-call-assets="web.assets_tests" t-if="'tests' in debug" defer_load="True" />
|
||||
</template>
|
||||
|
||||
<template id="web.layout" name="Web layout"><!DOCTYPE html>
|
||||
<html t-att="html_data or {}">
|
||||
<head>
|
||||
@@ -400,6 +410,7 @@
|
||||
<script type="text/javascript">
|
||||
var odoo = {
|
||||
csrf_token: "<t t-esc="request.csrf_token(None)"/>",
|
||||
debug: "<t t-esc="debug"/>",
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -422,6 +433,7 @@
|
||||
</script>
|
||||
<t t-call-assets="web.assets_common_minimal_js" t-css="false" defer_load="True"/>
|
||||
<t t-call-assets="web.assets_frontend_minimal_js" t-css="false" defer_load="True"/>
|
||||
<t t-call="web.conditional_assets_tests"/>
|
||||
<t t-call-assets="web.assets_common_lazy" t-css="false" lazy_load="True"/>
|
||||
<t t-call-assets="web.assets_frontend_lazy" t-css="false" lazy_load="True"/>
|
||||
</xpath>
|
||||
@@ -441,6 +453,7 @@
|
||||
<div class="container py-3">
|
||||
<div class="row">
|
||||
<div class="col-sm text-center text-sm-left text-muted">
|
||||
<t t-call="web.debug_icon"/>
|
||||
<span>Copyright &copy;</span> <span t-field="res_company.name" itemprop="name">Company name</span>
|
||||
</div>
|
||||
<div class="col-sm text-center text-sm-right">
|
||||
@@ -488,7 +501,7 @@
|
||||
|
||||
<template id="web.login" name="Login">
|
||||
<t t-call="web.login_layout">
|
||||
<form class="oe_login_form" role="form" t-attf-action="/web/login{{ '?debug' if debug else '' }}" method="post" onsubmit="this.action = this.action + location.hash">
|
||||
<form class="oe_login_form" role="form" t-attf-action="/web/login" method="post" onsubmit="this.action = this.action + location.hash">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
|
||||
<div class="form-group field-db" t-if="databases and len(databases) > 1">
|
||||
@@ -840,6 +853,7 @@
|
||||
<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.conditional_assets_tests"/>
|
||||
|
||||
<![CDATA[
|
||||
<!--[if lt IE 10]>
|
||||
@@ -864,6 +878,14 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="debug_icon" name="Debug Icon">
|
||||
<t t-if="debug">
|
||||
<t t-set="debug_mode_help" t-value="' (%s)' % debug if debug != '1' else ''"/>
|
||||
<a t-attf-href="?#{keep_query('*', debug='')}" t-attf-title="Debug mode is activated#{debug_mode_help}. Click here to exit debug mode."
|
||||
class="o_debug_mode"><span class="fa fa-bug"/></a>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
Optional Bundle for PDFJS lib
|
||||
Since PDFJS is quite huge (40000 lines), please only load it when it is necessary.
|
||||
|
||||
@@ -2,12 +2,12 @@ odoo.define('web_editor.ace', function (require) {
|
||||
'use strict';
|
||||
|
||||
var ajax = require('web.ajax');
|
||||
var config = require('web.config');
|
||||
var concurrency = require('web.concurrency');
|
||||
var core = require('web.core');
|
||||
var Dialog = require('web.Dialog');
|
||||
var Widget = require('web.Widget');
|
||||
var localStorage = require('web.local_storage');
|
||||
var session = require('web.session');
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
@@ -716,8 +716,8 @@ var ViewEditor = Widget.extend({
|
||||
_.each(this.$lists, function ($list, _type) { $list.toggleClass('d-none', type !== _type); });
|
||||
this.$lists[type].change();
|
||||
|
||||
this.$includeBundlesArea.toggleClass('d-none', this.currentType !== 'xml' || !session.debug);
|
||||
this.$includeAllSCSSArea.toggleClass('d-none', this.currentType !== 'scss' || !session.debug);
|
||||
this.$includeBundlesArea.toggleClass('d-none', this.currentType !== 'xml' || !config.isDebug());
|
||||
this.$includeAllSCSSArea.toggleClass('d-none', this.currentType !== 'scss' || !config.isDebug());
|
||||
this.$includeAllSCSSArea.find('[data-value="restricted"]').toggleClass('d-none', this.options.defaultBundlesRestriction.length === 0);
|
||||
this.$formatButton.toggleClass('d-none', this.currentType !== 'xml');
|
||||
},
|
||||
@@ -824,7 +824,7 @@ var ViewEditor = Widget.extend({
|
||||
}));
|
||||
}
|
||||
|
||||
if (!isSelected && session.debug && $elem.data('debug')) {
|
||||
if (!isSelected && config.isDebug() && $elem.data('debug')) {
|
||||
$div.append($('<span/>', {
|
||||
text: ' (' + $elem.data('debug') + ')',
|
||||
class: 'ml4 small text-muted',
|
||||
|
||||
@@ -19,7 +19,7 @@ var Wysiwyg = Widget.extend({
|
||||
wysiwyg_blur: '_onWysiwygBlur',
|
||||
},
|
||||
defaultOptions: {
|
||||
codeview: config.debug,
|
||||
codeview: config.isDebug(),
|
||||
recordInfo: {
|
||||
context: {},
|
||||
},
|
||||
|
||||
@@ -73,7 +73,7 @@ class WebSettingsDashboard(http.Controller):
|
||||
'share': {
|
||||
'server_version': release.version,
|
||||
'expiration_date': expiration_date,
|
||||
'debug': request.debug,
|
||||
'debug': request.session.debug,
|
||||
'demo_active': demo_active,
|
||||
},
|
||||
'company': {
|
||||
|
||||
@@ -2,7 +2,6 @@ odoo.define('web_settings_dashboard', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var framework = require('web.framework');
|
||||
var Widget = require('web.Widget');
|
||||
@@ -349,7 +348,6 @@ var DashboardShare = Widget.extend({
|
||||
_onClickForceDemo: function (ev) {
|
||||
ev.preventDefault();
|
||||
this.do_action('base.demo_force_install_action');
|
||||
config.debug = false;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -123,13 +123,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<a t-if="debug != true" class="oe_activate_debug_mode float-right" href="?debug" >Activate the developer mode</a>
|
||||
<br t-if="debug != true"/>
|
||||
<a t-if="debug != 'assets'" class="oe_activate_debug_mode float-right" href="?debug=assets" >Activate the developer mode (with assets)</a>
|
||||
<br t-if="debug != 'assets'"/>
|
||||
<a t-if="debug != false" class="oe_activate_debug_mode float-right" href="/web" >Deactivate the developer mode</a>
|
||||
<br t-if="debug != false"/>
|
||||
<a t-if="(debug) and !(demo_active)" class="oe_activate_debug_mode float-right o_web_settings_dashboard_force_demo" href="#">Load demo data</a>
|
||||
<div class="text-right">
|
||||
<a t-if="debug.indexOf('1') == -1" class="d-block" href="?debug=1">Activate the developer mode</a>
|
||||
<a t-if="debug.indexOf('assets') == -1" class="d-block" href="?debug=assets">Activate the developer mode (with assets)</a>
|
||||
<a t-if="debug.indexOf('tests') == -1" class="d-block" href="?debug=assets,tests">Activate the developer mode (with tests assets)</a>
|
||||
<a t-if="debug" class="d-block" href="?debug=">Deactivate the developer mode</a>
|
||||
<a t-if="debug and !demo_active" class="o_web_settings_dashboard_force_demo" href="#">Load demo data</a>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
odoo.define('web_tour.DebugManager', function (require) {
|
||||
odoo.define('web_tour.DebugManager.Backend', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require("web.core");
|
||||
var DebugManager = require('web.DebugManager');
|
||||
var DebugManager = require('web.DebugManager.Backend');
|
||||
var Dialog = require("web.Dialog");
|
||||
|
||||
var tour = require('web_tour.tour');
|
||||
|
||||
@@ -163,8 +163,7 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, {
|
||||
this.update();
|
||||
}).bind(this));
|
||||
|
||||
var url = session.debug ? $.param.querystring(tour.url, {debug: session.debug}) : tour.url;
|
||||
window.location.href = window.location.origin + url;
|
||||
window.location.href = window.location.origin + tour.url;
|
||||
} else {
|
||||
this.update();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-extend="WebClient.DebugManager.Global">
|
||||
<t t-extend="WebClient.DebugManager.Backend">
|
||||
<t t-jquery="a[data-action='select_view']" t-operation="after">
|
||||
<t t-if="manager._is_admin">
|
||||
<a t-if="manager.consume_tours_enabled" href="#" class="dropdown-item" data-action="consume_tours">Disable Tours</a>
|
||||
|
||||
@@ -44,7 +44,7 @@ class QWeb(models.AbstractModel):
|
||||
atts = super(QWeb, self)._post_processing_att(tagName, atts, options)
|
||||
|
||||
if options.get('inherit_branding') or options.get('rendering_bundle') or \
|
||||
options.get('edit_translations') or options.get('debug') or (request and request.debug):
|
||||
options.get('edit_translations') or options.get('debug') or (request and request.session.debug):
|
||||
return atts
|
||||
|
||||
website = request and getattr(request, 'website', None)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
odoo.define('website.debugManager', function (require) {
|
||||
'use strict';
|
||||
|
||||
var config = require('web.config');
|
||||
var DebugManager = require('web.DebugManager');
|
||||
var websiteNavbarData = require('website.navbar');
|
||||
|
||||
var DebugManagerMenu = websiteNavbarData.WebsiteNavbar.include({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
if (config.isDebug()) {
|
||||
new DebugManager(this).prependTo(this.$('.o_menu_systray'));
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
return DebugManagerMenu;
|
||||
});
|
||||
@@ -592,7 +592,7 @@ var ThemeCustomizeDialog = Dialog.extend({
|
||||
var $loading = $('<i/>', {class: 'fa fa-refresh fa-spin'});
|
||||
this.$modal.find('.modal-title').append($loading);
|
||||
|
||||
if (reload || config.debug === 'assets') {
|
||||
if (reload || config.isDebug('assets')) {
|
||||
window.location.href = $.param.querystring('/website/theme_customize_reload', {
|
||||
href: window.location.href,
|
||||
enable: (enable || []).join(','),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user