[REF] web, website: remove legacy DebugManager and use new DebugMenu
Previously, the debug manager was rewritten with the new webclient architecture, however, because the frontend did not have access to the new infrastructure (env and services), the old debug manager was kept and used only in the frontend. Since odoo/odoo#72675 the new env and services are now available, this commit simply uses the new debug manager in the frontend and removes the legacy code. closes odoo/odoo#74609 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
@@ -203,9 +203,6 @@ This module provides the core of the Odoo Web Client.
|
||||
'web/static/src/env.js',
|
||||
'web/static/src/core/utils/transitions.scss', # included early because used by other files
|
||||
'web/static/src/core/**/*',
|
||||
# TODO: split debug files correctly when converting frontend debug
|
||||
# manager so we don't need action-service and stuff
|
||||
('remove', 'web/static/src/core/debug/**/*'),
|
||||
'web/static/src/public/error_notifications.js',
|
||||
|
||||
'web/static/src/legacy/scss/base_frontend.scss',
|
||||
|
||||
@@ -61,8 +61,12 @@ class DebugContext {
|
||||
}
|
||||
|
||||
const debugContextSymbol = Symbol("debugContext");
|
||||
export function createDebugContext(env, { categories = [] } = {}) {
|
||||
return { [debugContextSymbol]: new DebugContext(env, categories) };
|
||||
}
|
||||
|
||||
export function useOwnDebugContext({ categories = [] } = {}) {
|
||||
useSubEnv({ [debugContextSymbol]: new DebugContext(useEnv(), categories) });
|
||||
useSubEnv(createDebugContext(useEnv(), { categories }));
|
||||
}
|
||||
|
||||
export function useCurrentDebugContext() {
|
||||
|
||||
@@ -4,85 +4,6 @@ import { browser } from "../browser/browser";
|
||||
import { routeToUrl } from "../browser/router_service";
|
||||
import { registry } from "../registry";
|
||||
|
||||
// @legacy
|
||||
import dialogs from "web.view_dialogs";
|
||||
import { ComponentAdapter } from "web.OwlCompatibility";
|
||||
|
||||
// Backend Debug Manager Items
|
||||
function runJSTestsItem({ env }) {
|
||||
const runTestsURL = browser.location.origin + "/web/tests?mod=*";
|
||||
return {
|
||||
type: "item",
|
||||
description: env._t("Run JS Tests"),
|
||||
href: runTestsURL,
|
||||
callback: () => {
|
||||
browser.open(runTestsURL);
|
||||
},
|
||||
sequence: 10,
|
||||
};
|
||||
}
|
||||
|
||||
function runJSTestsMobileItem({ env }) {
|
||||
const runTestsMobileURL = browser.location.origin + "/web/tests/mobile?mod=*";
|
||||
return {
|
||||
type: "item",
|
||||
description: env._t("Run JS Mobile Tests"),
|
||||
href: runTestsMobileURL,
|
||||
callback: () => {
|
||||
browser.open(runTestsMobileURL);
|
||||
},
|
||||
sequence: 20,
|
||||
};
|
||||
}
|
||||
|
||||
export function openViewItem({ env }) {
|
||||
async function onSelected(records) {
|
||||
const views = await env.services.orm.searchRead(
|
||||
"ir.ui.view",
|
||||
[["id", "=", records[0].id]],
|
||||
["name", "model", "type"],
|
||||
{ limit: 1 }
|
||||
);
|
||||
const view = views[0];
|
||||
view.type = view.type === "tree" ? "list" : view.type; // ignore tree view
|
||||
env.services.action.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
name: view.name,
|
||||
res_model: view.model,
|
||||
views: [[view.id, view.type]],
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: "item",
|
||||
description: env._t("Open View"),
|
||||
callback: () => {
|
||||
const adapterParent = new ComponentAdapter(null, { Component: owl.Component });
|
||||
const selectCreateDialog = new dialogs.SelectCreateDialog(adapterParent, {
|
||||
res_model: "ir.ui.view",
|
||||
title: env._t("Select a view"),
|
||||
disable_multiple_selection: true,
|
||||
domain: [
|
||||
["type", "!=", "qweb"],
|
||||
["type", "!=", "search"],
|
||||
],
|
||||
on_selected: onSelected,
|
||||
});
|
||||
|
||||
selectCreateDialog.open();
|
||||
},
|
||||
sequence: 40,
|
||||
};
|
||||
}
|
||||
|
||||
// Global Debug Manager Items
|
||||
function globalSeparator() {
|
||||
return {
|
||||
type: "separator",
|
||||
sequence: 400,
|
||||
};
|
||||
}
|
||||
|
||||
function activateAssetsDebugging({ env }) {
|
||||
return {
|
||||
type: "item",
|
||||
@@ -155,12 +76,6 @@ function leaveDebugMode({ env }) {
|
||||
registry
|
||||
.category("debug")
|
||||
.category("default")
|
||||
// Backend
|
||||
.add("runJSTestsItem", runJSTestsItem)
|
||||
.add("runJSTestsMobileItem", runJSTestsMobileItem)
|
||||
.add("openViewItem", openViewItem)
|
||||
// Global
|
||||
.add("globalSeparator", globalSeparator)
|
||||
.add("activateAssetsDebugging", activateAssetsDebugging)
|
||||
.add("regenerateAssets", regenerateAssets)
|
||||
.add("becomeSuperuser", becomeSuperuser)
|
||||
|
||||
@@ -8,7 +8,6 @@ const { Component } = owl;
|
||||
export class ProfilingItem extends Component {
|
||||
setup() {
|
||||
this.profiling = useService("profiling");
|
||||
this.actionService = useService("action");
|
||||
useBus(this.props.bus, "UPDATE", this.render);
|
||||
}
|
||||
|
||||
@@ -16,7 +15,13 @@ export class ProfilingItem extends Component {
|
||||
this.profiling.setParam(param, ev.target.value);
|
||||
}
|
||||
openProfiles() {
|
||||
this.actionService.doAction("base.action_menu_ir_profile");
|
||||
if (this.env.services.action) {
|
||||
// using doAction in the backend to preserve breadcrumbs and stuff
|
||||
this.env.services.action.doAction("base.action_menu_ir_profile");
|
||||
} else {
|
||||
// No action service means we are in the frontend.
|
||||
window.location = "/web/#action=base.action_menu_ir_profile";
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfilingItem.components = { DropdownItem };
|
||||
|
||||
@@ -18,6 +18,7 @@ import { UncaughtClientError, UncaughtCorsError, UncaughtPromiseError } from "./
|
||||
|
||||
const errorHandlerRegistry = registry.category("error_handlers");
|
||||
const errorDialogRegistry = registry.category("error_dialogs");
|
||||
const errorNotificationRegistry = registry.category("error_notifications");
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CORS errors
|
||||
@@ -71,7 +72,7 @@ errorHandlerRegistry.add("clientErrorHandler", clientErrorHandler, { sequence: 9
|
||||
* @param {Error} originalError
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function rpcErrorHandler(env, error, originalError) {
|
||||
export function rpcErrorHandler(env, error, originalError) {
|
||||
if (!(error instanceof UncaughtPromiseError)) {
|
||||
return false;
|
||||
}
|
||||
@@ -85,9 +86,16 @@ function rpcErrorHandler(env, error, originalError) {
|
||||
// error is here a RPCError
|
||||
error.unhandledRejectionEvent.preventDefault();
|
||||
const exceptionName = originalError.exceptionName;
|
||||
let ErrorComponent = null;
|
||||
if (exceptionName && errorDialogRegistry.contains(exceptionName)) {
|
||||
ErrorComponent = errorDialogRegistry.get(exceptionName);
|
||||
let ErrorComponent = originalError.Component;
|
||||
if (!ErrorComponent && exceptionName) {
|
||||
if (errorNotificationRegistry.contains(exceptionName)) {
|
||||
const notif = errorNotificationRegistry.get(exceptionName);
|
||||
env.services.notification.add(notif.message || originalError.data.message, notif);
|
||||
return true;
|
||||
}
|
||||
if (errorDialogRegistry.contains(exceptionName)) {
|
||||
ErrorComponent = errorDialogRegistry.get(exceptionName);
|
||||
}
|
||||
}
|
||||
|
||||
env.services.dialog.add(ErrorComponent || RPCErrorDialog, {
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<?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">
|
||||
<t t-set="_devtool_button_title">Open Developer Tools</t>
|
||||
<a role="button" href="#" class="o_debug_mode"
|
||||
t-att-title="_devtool_button_title + widget.debug_mode_help"
|
||||
t-att-aria-label="_devtool_button_title + widget.debug_mode_help"
|
||||
t-att-data-debug-mode="widget.debug_mode"
|
||||
data-toggle="dropdown" aria-expanded="false" tabindex="-1" data-display="static">
|
||||
<span class="fa fa-bug">
|
||||
<span class="profiling_items text-danger fa fa-circle"></span>
|
||||
</span>
|
||||
</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>
|
||||
<div class="dropdown-divider" role="separator"/>
|
||||
<span role="menuitem" class="dropdown-item profiling">
|
||||
<span class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="enable_profiling"/>
|
||||
<label class="custom-control-label" for="enable_profiling">
|
||||
Enable profiling
|
||||
<span class="profiling_items text-danger fa fa-circle"></span>
|
||||
</label>
|
||||
<i id="open_profiling_button" class="open_profiling float-right fa fa-list fa-lg"></i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="profiling_items">
|
||||
<span role="menuitem" class="dropdown-item profiling">
|
||||
<span class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input profile_switch" id="profile_sql"/>
|
||||
<label class="custom-control-label" for="profile_sql">Record sql</label>
|
||||
</span>
|
||||
</span>
|
||||
<span role="menuitem" class="dropdown-item profiling">
|
||||
<span class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input profile_switch" id="profile_traces_async"/>
|
||||
<label class="custom-control-label" for="profile_traces_async">Record traces</label>
|
||||
</span>
|
||||
</span>
|
||||
<span role="menuitem" class="dropdown-item profiling profile_traces_async">
|
||||
<span class="">
|
||||
<div class="input-group input-group-sm">
|
||||
<div class="input-group-prepend">
|
||||
<div class="input-group-text">Interval</div>
|
||||
</div>
|
||||
<select id="profile_traces_async_interval" class="profile_param form-control">
|
||||
<option value="">Default</option>
|
||||
<option value="0.001">0.001</option>
|
||||
<option value="0.01">0.01</option>
|
||||
<option value="0.1">0.1</option>
|
||||
<option value="1">1</option>
|
||||
</select>
|
||||
<!--input type="number" step="any" min="0.001" max="10" value="0.001" /-->
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,194 +0,0 @@
|
||||
odoo.define('web.DebugManager', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var session = require('web.session');
|
||||
var utils = require('web.utils');
|
||||
var Widget = require('web.Widget');
|
||||
const { session: sessionInfo } = require('@web/session');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
|
||||
/**
|
||||
* DebugManager base + general features (applicable to any context)
|
||||
*/
|
||||
var DebugManager = Widget.extend({
|
||||
template: "WebClient.DebugManager",
|
||||
xmlDependencies: ['/web/static/src/legacy/frontend/debug_manager/debug.xml'],
|
||||
events: {
|
||||
"click a[data-action]": "perform_callback",
|
||||
"click .profiling": "handle_profiling",
|
||||
"change .profiling_param": "handle_profiling",
|
||||
},
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._events = null;
|
||||
var debug = odoo.debug;
|
||||
this.debug_mode = debug;
|
||||
this.debug_mode_help = debug && debug !== '1' ? ' (' + debug + ')' : '';
|
||||
this.profile_session = sessionInfo.profile_session || false;
|
||||
this.profile_collectors = sessionInfo.profile_collectors;
|
||||
if (! Array.isArray(this.profile_collectors)) {
|
||||
this.profile_collectors = ['sql', 'traces_async']; // use default value when not defined in session.
|
||||
}
|
||||
this.profile_params = sessionInfo.profile_params || {};
|
||||
},
|
||||
start: function () {
|
||||
core.bus.on('rpc:result', this, function (req, resp) {
|
||||
this._debug_events(resp.debug);
|
||||
});
|
||||
|
||||
this.$dropdown = this.$(".o_debug_dropdown");
|
||||
// whether the current user is an administrator
|
||||
this._is_admin = session.is_system;
|
||||
return Promise.resolve(
|
||||
this._super()
|
||||
).then(function () {
|
||||
return this.update();
|
||||
}.bind(this));
|
||||
},
|
||||
/**
|
||||
* Calls the appropriate callback when clicking on a Debug option
|
||||
*/
|
||||
perform_callback: function (evt) {
|
||||
evt.preventDefault();
|
||||
var params = $(evt.target).data();
|
||||
var callback = params.action;
|
||||
|
||||
if (callback && this[callback]) {
|
||||
// Perform the callback corresponding to the option
|
||||
this[callback](params, evt);
|
||||
} else {
|
||||
console.warn("No handler for ", callback);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Manage profiling actions.
|
||||
* open_profiling_button should not prevent dropdown from closing
|
||||
* other actions should keep dropdown open and interact with backend to enable or disable profiling features.
|
||||
* The main switch, 'enable_profiling' is a special case since it will create or remove a profile session
|
||||
*/
|
||||
handle_profiling: async function (evt) {
|
||||
const $target = $(evt.target);
|
||||
const target = $target[0];
|
||||
if (target.id === 'open_profiling_button') {
|
||||
this.do_action('base.action_menu_ir_profile');
|
||||
return;
|
||||
}
|
||||
evt.stopPropagation(); // prevent dropdown from closing
|
||||
const kwargs = {
|
||||
params: {}
|
||||
};
|
||||
if (target.nodeName == "LABEL") {
|
||||
return
|
||||
}
|
||||
kwargs.profile = $('#enable_profiling')[0].checked;
|
||||
kwargs.collectors = $('input.profile_switch').toArray().filter(i => i.checked).map(i => i.id.replace(/^profile_/, ''));
|
||||
$('.profile_param').toArray().forEach(i => kwargs.params[i.id.replace(/^profile_/, '')] = i.value);
|
||||
|
||||
try {
|
||||
const resp = await this._rpc({
|
||||
model: 'ir.profile',
|
||||
method: 'set_profiling',
|
||||
kwargs: kwargs,
|
||||
});
|
||||
this.profile_session = resp.session;
|
||||
this.profile_collectors = resp.collectors;
|
||||
this.profile_params = resp.params;
|
||||
} catch (e) {
|
||||
this.profite_session = false;
|
||||
}
|
||||
this.update_profiling_state();
|
||||
},
|
||||
/**
|
||||
* Update the profiling dropdown menu state after any change to synchronyze server session and client
|
||||
* This is mainly usefull to avoid desync in case of multiple tabs
|
||||
*/
|
||||
update_profiling_state: function () {
|
||||
const self = this;
|
||||
this.$('#enable_profiling')[0].checked = Boolean(this.profile_session);
|
||||
this.$('.profile_switch').each(function () {
|
||||
this.checked = (self.profile_collectors.indexOf(this.id.replace(/^profile_/, '')) !== -1);
|
||||
const params = self.$('.' + this.id);
|
||||
params.toggleClass('d-none', !this.checked);
|
||||
params.each(function () {
|
||||
const params_input = $(this).find('.profile_param')[0];
|
||||
params_input.value = self.profile_params[params_input.id.replace(/^profile_/, '')] || '';
|
||||
});
|
||||
});
|
||||
this.$profiling_items.toggleClass('d-none', !this.profile_session);
|
||||
},
|
||||
_debug_events: function (events) {
|
||||
if (!this._events) {
|
||||
return;
|
||||
}
|
||||
if (events && events.length) {
|
||||
this._events.push(events);
|
||||
}
|
||||
this.trigger('update-stats', this._events);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the debug manager: reinserts all "universal" controls
|
||||
*/
|
||||
update: function () {
|
||||
this.$dropdown
|
||||
.empty()
|
||||
.append(QWeb.render('WebClient.DebugManager.Global', {
|
||||
manager: this,
|
||||
}));
|
||||
this.$profiling_items = this.$(".profiling_items");
|
||||
|
||||
this.update_profiling_state();
|
||||
return Promise.resolve();
|
||||
},
|
||||
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
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
regenerateAssets: function () {
|
||||
var self = this;
|
||||
var domain = utils.assetsDomain();
|
||||
this._rpc({
|
||||
model: 'ir.attachment',
|
||||
method: 'search',
|
||||
args: [domain],
|
||||
}).then(function (ids) {
|
||||
self._rpc({
|
||||
model: 'ir.attachment',
|
||||
method: 'unlink',
|
||||
args: [ids],
|
||||
}).then(window.location.reload());
|
||||
});
|
||||
},
|
||||
leave_debug_mode: function () {
|
||||
var qs = $.deparam.querystring();
|
||||
qs.debug = '';
|
||||
window.location.search = '?' + $.param(qs);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {string} model
|
||||
* @param {string} operation
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
_checkAccessRight(model, operation) {
|
||||
return this._rpc({
|
||||
model: model,
|
||||
method: 'check_access_rights',
|
||||
kwargs: {operation, raise_exception: false},
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
return DebugManager;
|
||||
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
.o_debug_mode .fa { /* Active profiling red dot should be overed on the bottom right corner of the bug icon*/
|
||||
position: relative;
|
||||
.fa {
|
||||
position: absolute;
|
||||
bottom: -0.1em;
|
||||
right: -0.4em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.open_profiling { /* Fix dropdown-item padding missing because of float: right*/
|
||||
padding: 3px;
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { RPCErrorDialog } from "../core/errors/error_dialogs";
|
||||
import { ConnectionLostError, RPCError } from "../core/network/rpc_service";
|
||||
import { lostConnectionHandler } from "@web/core/errors/error_handlers";
|
||||
import { lostConnectionHandler, rpcErrorHandler } from "@web/core/errors/error_handlers";
|
||||
|
||||
const errorNotificationRegistry = registry.category("error_notifications");
|
||||
const errorDialogRegistry = registry.category("error_dialogs");
|
||||
const errorHandlerRegistry = registry.category("error_handlers");
|
||||
|
||||
/**
|
||||
@@ -32,41 +29,18 @@ function legacyRPCErrorHandler(env, error, originalError) {
|
||||
(originalError.message instanceof RPCError ||
|
||||
originalError.message instanceof ConnectionLostError)
|
||||
) {
|
||||
if (originalError.message instanceof ConnectionLostError) {
|
||||
return lostConnectionHandler(env, error, originalError.message);
|
||||
}
|
||||
const event = originalError.event;
|
||||
originalError = originalError.message;
|
||||
error.unhandledRejectionEvent.preventDefault();
|
||||
if (event.isDefaultPrevented()) {
|
||||
// in theory, here, event was already handled
|
||||
error.unhandledRejectionEvent.preventDefault();
|
||||
return true;
|
||||
}
|
||||
event.preventDefault();
|
||||
const exceptionName = originalError.exceptionName;
|
||||
let ErrorComponent = originalError.Component;
|
||||
if (!ErrorComponent && exceptionName) {
|
||||
if (errorNotificationRegistry.contains(exceptionName)) {
|
||||
const notif = errorNotificationRegistry.get(exceptionName);
|
||||
env.services.notification.add(notif.message || originalError.data.message, notif);
|
||||
return true;
|
||||
}
|
||||
if (errorDialogRegistry.contains(exceptionName)) {
|
||||
ErrorComponent = errorDialogRegistry.get(exceptionName);
|
||||
}
|
||||
if (originalError instanceof ConnectionLostError) {
|
||||
return lostConnectionHandler(env, error, originalError);
|
||||
}
|
||||
|
||||
env.services.dialog.add(ErrorComponent || RPCErrorDialog, {
|
||||
traceback: originalError.traceback || originalError.stack,
|
||||
message: originalError.message,
|
||||
name: originalError.name,
|
||||
exceptionName: originalError.exceptionName,
|
||||
data: originalError.data,
|
||||
subType: originalError.subType,
|
||||
code: originalError.code,
|
||||
type: originalError.type,
|
||||
});
|
||||
return true;
|
||||
return rpcErrorHandler(env, error, originalError);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/** @odoo-module */
|
||||
import { registry } from "@web/core/registry";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
|
||||
import dialogs from "web.view_dialogs";
|
||||
import { ComponentAdapter } from "web.OwlCompatibility";
|
||||
|
||||
function runJSTestsItem({ env }) {
|
||||
const runTestsURL = browser.location.origin + "/web/tests?mod=*";
|
||||
return {
|
||||
type: "item",
|
||||
description: env._t("Run JS Tests"),
|
||||
href: runTestsURL,
|
||||
callback: () => {
|
||||
browser.open(runTestsURL);
|
||||
},
|
||||
sequence: 10,
|
||||
};
|
||||
}
|
||||
|
||||
function runJSTestsMobileItem({ env }) {
|
||||
const runTestsMobileURL = browser.location.origin + "/web/tests/mobile?mod=*";
|
||||
return {
|
||||
type: "item",
|
||||
description: env._t("Run JS Mobile Tests"),
|
||||
href: runTestsMobileURL,
|
||||
callback: () => {
|
||||
browser.open(runTestsMobileURL);
|
||||
},
|
||||
sequence: 20,
|
||||
};
|
||||
}
|
||||
|
||||
export function openViewItem({ env }) {
|
||||
async function onSelected(records) {
|
||||
const views = await env.services.orm.searchRead(
|
||||
"ir.ui.view",
|
||||
[["id", "=", records[0].id]],
|
||||
["name", "model", "type"],
|
||||
{ limit: 1 }
|
||||
);
|
||||
const view = views[0];
|
||||
view.type = view.type === "tree" ? "list" : view.type; // ignore tree view
|
||||
env.services.action.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
name: view.name,
|
||||
res_model: view.model,
|
||||
views: [[view.id, view.type]],
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: "item",
|
||||
description: env._t("Open View"),
|
||||
callback: () => {
|
||||
const adapterParent = new ComponentAdapter(null, { Component: owl.Component });
|
||||
const selectCreateDialog = new dialogs.SelectCreateDialog(adapterParent, {
|
||||
res_model: "ir.ui.view",
|
||||
title: env._t("Select a view"),
|
||||
disable_multiple_selection: true,
|
||||
domain: [
|
||||
["type", "!=", "qweb"],
|
||||
["type", "!=", "search"],
|
||||
],
|
||||
on_selected: onSelected,
|
||||
});
|
||||
|
||||
selectCreateDialog.open();
|
||||
},
|
||||
sequence: 40,
|
||||
};
|
||||
}
|
||||
|
||||
// This separates the items defined above from global items that aren't webclient-only
|
||||
function globalSeparator() {
|
||||
return {
|
||||
type: "separator",
|
||||
sequence: 400,
|
||||
};
|
||||
}
|
||||
|
||||
registry
|
||||
.category("debug")
|
||||
.category("default")
|
||||
.add("runJSTestsItem", runJSTestsItem)
|
||||
.add("runJSTestsMobileItem", runJSTestsMobileItem)
|
||||
.add("globalSeparator", globalSeparator)
|
||||
.add("openViewItem", openViewItem);
|
||||
@@ -14,7 +14,7 @@ import { makeTestEnv, prepareRegistriesWithCleanup } from "../../helpers/mock_en
|
||||
import { makeFakeDialogService, makeFakeLocalizationService } from "../../helpers/mock_services";
|
||||
import { click, getFixture, legacyExtraNextTick, patchWithCleanup } from "../../helpers/utils";
|
||||
import { createWebClient, doAction, getActionManagerServerData } from "../../webclient/helpers";
|
||||
import { openViewItem } from "@web/core/debug/debug_menu_items";
|
||||
import { openViewItem } from "@web/webclient/debug_items";
|
||||
import { editView, editSearchView } from "@web/legacy/debug_manager";
|
||||
|
||||
const { Component, mount, tags } = owl;
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
odoo.define('web.debugManagerTests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var testUtils = require('web.test_utils');
|
||||
var FormView = require('web.FormView');
|
||||
|
||||
var createDebugManager = testUtils.createDebugManager;
|
||||
|
||||
QUnit.module('DebugManager', {}, function () {
|
||||
|
||||
QUnit.skip("list: edit view menu item", async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var debugManager = await createDebugManager();
|
||||
|
||||
await debugManager.appendTo($('#qunit-fixture'));
|
||||
|
||||
// Simulate update debug manager from web client
|
||||
var action = {
|
||||
views: [{
|
||||
displayName: "List",
|
||||
fieldsView: {
|
||||
view_id: 1,
|
||||
},
|
||||
type: "list",
|
||||
}],
|
||||
};
|
||||
var view = {
|
||||
viewType: "list",
|
||||
};
|
||||
await testUtils.nextTick();
|
||||
await debugManager.update('action', action, view);
|
||||
|
||||
var $editView = debugManager.$('a[data-action=edit][data-model="ir.ui.view"]');
|
||||
assert.strictEqual($editView.length, 1, "should have edit view menu item");
|
||||
assert.strictEqual($editView.text().trim(), "Edit View: List",
|
||||
"should have correct menu item text for editing view");
|
||||
assert.strictEqual($editView.data('id'), 1, "should have correct view_id");
|
||||
|
||||
debugManager.destroy();
|
||||
});
|
||||
|
||||
QUnit.skip("form: Manage Attachments option", async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var debugManager = await createDebugManager({
|
||||
intercepts: {
|
||||
do_action: function (event) {
|
||||
assert.deepEqual(event.data.action, {
|
||||
context: {
|
||||
default_res_model: "test.model",
|
||||
default_res_id: 5,
|
||||
},
|
||||
domain: [["res_model", "=", "test.model"],["res_id", "=", 5]],
|
||||
name: "Manage Attachments",
|
||||
res_model: "ir.attachment",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "list"],[false, "form"]],
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
await debugManager.appendTo($('#qunit-fixture'));
|
||||
|
||||
// Simulate update debug manager from web client
|
||||
var action = {
|
||||
views: [{
|
||||
displayName: "Form",
|
||||
fieldsView: {
|
||||
view_id: 2,
|
||||
},
|
||||
type: "form",
|
||||
}],
|
||||
res_model: "test.model",
|
||||
};
|
||||
var view = {
|
||||
viewType: "form",
|
||||
getSelectedIds: function () {
|
||||
return [5];
|
||||
},
|
||||
};
|
||||
await debugManager.update('action', action, view);
|
||||
|
||||
var $attachmentMenu = debugManager.$('a[data-action=get_attachments]');
|
||||
assert.strictEqual($attachmentMenu.length, 1, "should have Manage Attachments menu item");
|
||||
assert.strictEqual($attachmentMenu.text().trim(), "Manage Attachments",
|
||||
"should have correct menu item text");
|
||||
await testUtils.dom.click(debugManager.$('> a')); // open dropdown
|
||||
await testUtils.dom.click($attachmentMenu);
|
||||
|
||||
debugManager.destroy();
|
||||
});
|
||||
|
||||
QUnit.skip("Debug: Set defaults with right model", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
/* Click on debug > set default,
|
||||
* set some defaults, click on save
|
||||
* model and some other data should be sent to server
|
||||
*/
|
||||
|
||||
// We'll need a full blown architecture with some data
|
||||
var data = {
|
||||
partner: {
|
||||
fields: {
|
||||
foo: {string: "Foo", type: "char", default: "My little Foo Value"},
|
||||
},
|
||||
records: [{
|
||||
id: 1,
|
||||
foo: "yop",
|
||||
}]
|
||||
},
|
||||
'ir.default': { // We just need this to be defined
|
||||
fields: {},
|
||||
},
|
||||
};
|
||||
|
||||
var form = await testUtils.createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: data,
|
||||
arch: '<form string="Partners">' +
|
||||
'<field name="foo" />' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
});
|
||||
|
||||
// Now the real tested component
|
||||
var debugManager = await createDebugManager({
|
||||
data: data,
|
||||
mockRPC: function (route, args) {
|
||||
if (route == "/web/dataset/call_kw/ir.default/set") {
|
||||
assert.deepEqual(args.args, ["partner", "foo", "yop", true, true, false],
|
||||
'Model, field, value and booleans for current user/company should have been passed');
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
await debugManager.appendTo($('#qunit-fixture'));
|
||||
|
||||
// Simulate update debug manager from web client
|
||||
var action = {
|
||||
controlPanelFieldsView: {},
|
||||
views: [{
|
||||
fieldsView: {
|
||||
view_id: 1,
|
||||
model: 'partner',
|
||||
type: 'form',
|
||||
},
|
||||
type: "form",
|
||||
}],
|
||||
res_model: 'partner',
|
||||
};
|
||||
|
||||
// We are all set
|
||||
await debugManager.update('action', action, form);
|
||||
|
||||
// click on set_defaults dropdown
|
||||
await testUtils.dom.click(debugManager.$('> a')); // open dropdown
|
||||
await testUtils.dom.click(debugManager.$('a[data-action="set_defaults"]'));
|
||||
var $modal = $('.modal-content');
|
||||
assert.strictEqual($modal.length, 1, 'One modal present');
|
||||
|
||||
$modal.find('select[id=formview_default_fields] option[value=foo]').prop('selected', true);
|
||||
|
||||
// Save
|
||||
await testUtils.dom.click($modal.find('.modal-footer button').eq(1));
|
||||
|
||||
form.destroy();
|
||||
debugManager.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -205,7 +205,7 @@
|
||||
'website/static/src/js/form_editor_registry.js',
|
||||
'website/static/src/js/menu/content.js',
|
||||
'website/static/src/js/menu/customize.js',
|
||||
'website/static/src/js/menu/debug_manager.js',
|
||||
'website/static/src/js/menu/debug_menu.js',
|
||||
'website/static/src/js/menu/edit.js',
|
||||
'website/static/src/js/menu/mobile_view.js',
|
||||
'website/static/src/js/menu/new_content.js',
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
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;
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/** @odoo-module */
|
||||
import { registry } from "@web/core/registry";
|
||||
import { DebugMenu } from "@web/core/debug/debug_menu";
|
||||
import { createDebugContext } from "@web/core/debug/debug_context";
|
||||
|
||||
const debugMenuService = {
|
||||
dependencies: ["orm"],
|
||||
start(env) {
|
||||
Object.assign(env, createDebugContext(env, { categories: ["default"] }));
|
||||
const systray = document.querySelector('.o_menu_systray');
|
||||
if (systray) {
|
||||
owl.mount(DebugMenu, {
|
||||
target: systray,
|
||||
position: 'first-child',
|
||||
env,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
registry.category("services").add("website_debug_menu", debugMenuService);
|
||||
Reference in New Issue
Block a user