[REF] web,*: remove legacy DataManager

The DataManager is no longer used as all views and models have
been converted to wowl.

Part of task~3439226

closes odoo/odoo#129725

Related: odoo/enterprise#44611
Signed-off-by: Pierre Rousseau (pro) <pro@odoo.com>
This commit is contained in:
Aaron Bohy
2023-07-26 13:22:13 +02:00
parent e9d3e27792
commit cf4e844aca
13 changed files with 2 additions and 493 deletions
-2
View File
@@ -179,9 +179,7 @@ For more specific needs, you may also assign custom-defined actions
'web/static/src/legacy/js/core/misc.js',
'web/static/src/legacy/js/env.js',
'web/static/src/legacy/js/owl_compatibility.js',
'web/static/src/legacy/js/services/data_manager.js',
'web/static/src/legacy/js/services/session.js',
'web/static/src/legacy/legacy_load_views.js',
'web/static/src/legacy/utils.js',
'mail/static/src/core/common/**/*',
@@ -91,7 +91,6 @@
'web/static/src/legacy/legacy_service_provider.js',
'web/static/src/legacy/legacy_dialog.js',
'web/static/src/legacy/legacy_load_views.js',
'web/static/src/legacy/legacy_promise_error_handler.js',
'web/static/src/legacy/legacy_rpc_error_handler.js',
'web/static/src/legacy/root_widget.js',
@@ -109,7 +108,6 @@
'web/static/src/legacy/js/fields/relational_fields.js',
'web/static/src/legacy/js/fields/field_registry.js',
'web/static/src/legacy/js/fields/field_utils.js',
'web/static/src/legacy/js/services/data_manager.js',
'web/static/src/legacy/js/services/session.js',
'web/static/src/legacy/js/widgets/date_picker.js',
'web/static/src/legacy/js/env.js',
-1
View File
@@ -157,7 +157,6 @@
('remove', 'web/static/src/legacy/js/libs/**/*'),
('remove', 'web/static/src/legacy/js/public/**/*'),
'web/static/src/legacy/backend_utils.js',
'web/static/src/legacy/legacy_load_views.js',
'web/static/src/legacy/legacy_service_provider.js',
'web/static/src/legacy/utils.js',
'web/static/src/search/**/*',
-2
View File
@@ -142,7 +142,6 @@
'web/static/src/legacy/legacy_service_provider.js',
'web/static/src/legacy/legacy_dialog.js',
'web/static/src/legacy/legacy_load_views.js',
'web/static/src/legacy/legacy_promise_error_handler.js',
'web/static/src/legacy/legacy_rpc_error_handler.js',
'web/static/src/legacy/root_widget.js',
@@ -161,7 +160,6 @@
'web/static/src/legacy/js/fields/field_registry.js',
'web/static/src/legacy/js/fields/field_registration.js',
'web/static/src/legacy/js/fields/field_utils.js',
'web/static/src/legacy/js/services/data_manager.js',
'web/static/src/legacy/js/services/session.js',
'web/static/src/legacy/js/widgets/date_picker.js',
'web/static/src/legacy/js/env.js',
-2
View File
@@ -218,7 +218,6 @@ This module provides the core of the Odoo Web Client.
'web/static/src/legacy/legacy_service_provider.js',
'web/static/src/legacy/legacy_dialog.js',
'web/static/src/legacy/legacy_load_views.js',
'web/static/src/legacy/legacy_promise_error_handler.js',
'web/static/src/legacy/legacy_rpc_error_handler.js',
'web/static/src/legacy/root_widget.js',
@@ -229,7 +228,6 @@ This module provides the core of the Odoo Web Client.
'web/static/src/legacy/js/core/context.js',
'web/static/src/legacy/js/core/misc.js',
'web/static/src/legacy/js/fields/*',
'web/static/src/legacy/js/services/data_manager.js',
'web/static/src/legacy/js/services/session.js',
'web/static/src/legacy/js/widgets/date_picker.js',
'web/static/src/legacy/js/env.js',
+1 -2
View File
@@ -5,10 +5,9 @@
*/
import commonEnv from 'web.commonEnv';
import dataManager from 'web.data_manager';
import { blockUI, unblockUI } from "web.framework";
const env = Object.assign(commonEnv, { dataManager });
const env = commonEnv;
env.services = Object.assign(env.services, { blockUI, unblockUI });
export default env;
@@ -262,19 +262,6 @@
if (payload.callback) {
payload.callback(this.env.session);
}
} else if (evType === 'load_views') {
const params = {
model: payload.modelName,
context: payload.context,
views_descr: payload.views,
};
this.env.dataManager
.load_views(params, payload.options || {})
.then(payload.on_success);
} else if (evType === 'load_filters') {
return this.env.dataManager
.load_filters(payload)
.then(payload.on_success);
} else {
payload.__targetWidget = ev.target;
this.trigger(evType.replace(/_/g, '-'), payload);
@@ -1,226 +0,0 @@
/** @odoo-module alias=web.data_manager **/
import config from "web.config";
import core from "web.core";
import rpc from "web.rpc";
import session from "web.session";
import { generateLegacyLoadViewsResult } from "@web/legacy/legacy_load_views";
const DataManager = core.Class.extend({
init: function () {
this._init_cache();
core.bus.on('clear_cache', this, this.invalidate.bind(this));
},
_init_cache: function () {
this._cache = {
actions: {},
filters: {},
views: {},
};
},
/**
* Invalidates the whole cache. Only works when not triggered by itself.
* Suggestion: could be refined to invalidate some part of the cache
*
* @param {Object} [dataManager]
*/
invalidate: function (dataManager) {
if (dataManager === this) {
return;
}
session.invalidateCacheKey('load_menus');
this._init_cache();
},
/**
* Loads an action from its id or xmlid.
*
* @param {int|string} [action_id] the action id or xmlid
* @param {Object} [additional_context] used to load the action
* @return {Promise} resolved with the action whose id or xmlid is action_id
*/
load_action: function (action_id, additional_context) {
var self = this;
var key = this._gen_key(action_id, additional_context || {});
if (config.isDebug('assets') || !this._cache.actions[key]) {
this._cache.actions[key] = rpc.query({
route: "/web/action/load",
params: {
action_id: action_id,
additional_context: additional_context,
},
}).then(function (action) {
self._cache.actions[key] = action.no_cache ? null : self._cache.actions[key];
return action;
}).guardedCatch(() => this._invalidate('actions', key));
}
return this._cache.actions[key].then(function (action) {
return $.extend(true, {}, action);
});
},
/**
* Loads various information concerning views: fields_view for each view,
* the fields of the corresponding model, and optionally the filters.
*
* @param {Object} params
* @param {String} params.model
* @param {Object} params.context
* @param {Array} params.views_descr array of [view_id, view_type]
* @param {Object} [options={}] dictionary of various options:
* - options.load_filters: whether or not to load the filters,
* - options.action_id: the action_id (required to load filters),
* - options.toolbar: whether or not a toolbar will be displayed,
* @return {Promise} resolved with the requested views information
*/
load_views: async function ({ model, context, views_descr } , options = {}) {
const viewsKey = this._gen_key(model, views_descr, options, context);
const filtersKey = this._gen_key(model, options.action_id);
const withFilters = Boolean(options.load_filters);
const shouldLoadViews = config.isDebug('assets') || !this._cache.views[viewsKey];
const shouldLoadFilters = config.isDebug('assets') || (
withFilters && !this._cache.filters[filtersKey]
);
if (shouldLoadViews) {
// Views info should be loaded
options.load_filters = shouldLoadFilters;
if (config.device.isMobile) {
options.mobile = config.device.isMobile;
}
this._cache.views[viewsKey] = rpc.query({
args: [],
kwargs: { context, options, views: views_descr },
model,
method: 'get_views',
}).then(result => {
const { models, views } = result;
result = generateLegacyLoadViewsResult(model, views, models);
// Freeze the fields dict as it will be shared between views and
// no one should edit it
// utils.deepFreeze(result.fields); // OWL issue regarding proxies and frozen objects https://github.com/odoo/owl/issues/1158
for (const [ /* viewId */ , viewType] of views_descr) {
const fvg = result.fields_views[viewType];
fvg.viewFields = fvg.fields;
fvg.fields = result.fields;
}
// Insert filters, if any, into the filters cache
if (shouldLoadFilters) {
this._cache.filters[filtersKey] = Promise.resolve(result.filters);
}
return result.fields_views;
}).guardedCatch(() => this._invalidate('views', viewsKey));
}
const result = await this._cache.views[viewsKey];
if (withFilters && result.search) {
if (shouldLoadFilters) {
await this.load_filters({
actionId: options.action_id,
context,
forceReload: false,
modelName: model,
});
}
result.search.favoriteFilters = await this._cache.filters[filtersKey];
}
return result;
},
/**
* Loads the filters of a given model and optional action id.
*
* @param {Object} params
* @param {number} params.actionId
* @param {Object} params.context
* @param {boolean} [params.forceReload=true] can be set to false to prevent forceReload
* @param {string} params.modelName
* @return {Promise} resolved with the requested filters
*/
load_filters: function (params) {
const key = this._gen_key(params.modelName, params.actionId);
const forceReload = params.forceReload !== false && config.isDebug('assets');
if (forceReload || !this._cache.filters[key]) {
this._cache.filters[key] = rpc.query({
args: [params.modelName, params.actionId],
kwargs: {
context: params.context || {},
// get_context() de dataset
},
model: 'ir.filters',
method: 'get_filters',
}).guardedCatch(() => this._invalidate('filters', key));
}
return this._cache.filters[key];
},
/**
* Calls 'create_or_replace' on 'ir_filters'.
*
* @param {Object} [filter] the filter description
* @return {Promise} resolved with the id of the created or replaced filter
*/
create_filter: function (filter) {
return rpc.query({
args: [filter],
model: 'ir.filters',
method: 'create_or_replace',
})
.then(filterId => {
const filtersKey = this._gen_key(filter.model_id, filter.action_id);
this._invalidate('filters', filtersKey);
return filterId;
});
},
/**
* Calls 'unlink' on 'ir_filters'.
*
* @param {integer} filterId Id of the filter to remove
* @return {Promise}
*/
delete_filter: function (filterId) {
return rpc.query({
args: [filterId],
model: 'ir.filters',
method: 'unlink',
})
// Invalidate the whole cache since we have no idea where the filter came from.
.then(() => this._invalidate('filters'));
},
/**
* Private function that generates a cache key from its arguments
*/
_gen_key: function () {
return Array.prototype.slice.call(arguments).map((arg) => {
if (!arg) {
return false;
}
return typeof arg === "object" ? JSON.stringify(arg) : arg;
}).join(",");
},
/**
* Invalidate a cache entry or a whole cache section.
*
* @private
* @param {string} section
* @param {string} key
*/
_invalidate(section, key) {
core.bus.trigger("clear_cache", this);
if (key) {
delete this._cache[section][key];
} else {
this._cache[section] = {};
}
},
});
var data_manager = new DataManager();
export default data_manager;
@@ -1,126 +0,0 @@
/** @odoo-module **/
const xmlSerializer = new XMLSerializer();
const domParser = new DOMParser();
function traverse(tree, f) {
if (f(tree)) {
Object.values(tree.children || {}).forEach((c) => {
traverse(c, f);
});
}
}
/**
* Given the result of a get_views, produces the equivalent of what was previously
* returned, such that legacy views continue working as before without any adaptations.
*
* @param {string} resModel
* @param {Object} views "views" key of the result of a get_views
* @param {Object} models maps of model name -> fields_get
* @returns {Object} similar to what was previously returned by load_views
*/
export function generateLegacyLoadViewsResult(resModel, views, models) {
const legacyFieldViews = {
fields: models[resModel],
fields_views: {},
};
if (views.search && views.search.filters) {
legacyFieldViews.filters = views.search.filters;
}
for (const viewType in views) {
const { arch, toolbar, id } = views[viewType];
const { arch: processedArch, viewFields } = processArch(arch, viewType, resModel, models);
legacyFieldViews.fields_views[viewType] = {
arch: processedArch,
fields: viewFields,
model: resModel,
toolbar,
view_id: id,
};
}
return legacyFieldViews;
}
/**
* Given an arch with inline x2many subviews, generates the object of fields
* appearing in the arch, with a "views" key that contains the descriptions of
* their subviews (whose archs are removed from the main arch).
*
* @param {string} arch
* @param {string} viewType
* @param {string} resModel
* @param {Object} models
* @returns {Object} the processed arch and the (view) fields description
*/
export function processArch(arch, viewType, resModel, models) {
const viewFields = {};
const archDoc = domParser.parseFromString(arch, "text/xml").documentElement;
traverse(archDoc, function (node) {
if (node.nodeType === 3) {
return false;
}
if (node.tagName === "field") {
const fieldName = node.getAttribute("name");
if (viewFields[fieldName] === undefined) {
const viewFieldsInfo = models[resModel][fieldName];
const nodeString = node.getAttribute("string");
if (nodeString === null && viewFieldsInfo === undefined) {
throw new Error(
`Missing field string information for the field '${fieldName}' from the '${resModel}' model`
);
}
viewFields[fieldName] = {
...viewFieldsInfo,
string: nodeString || viewFieldsInfo.string,
views: {},
};
}
// extract subviews
// note: this should only be done for form views, because x2many subviews doesn't make
// any sense in any other view type, but sometimes in the codebase, people inline
// subviews in list views (e.g. sale.order.form.inherit.sale.product.configurator) so
// we remove them from the main arch anyway, to mock the previous behavior
if (viewType === "form" || viewType === "list") {
const coModel = models[resModel][fieldName].relation;
const views = {};
for (const childNode of [...node.children]) {
const viewType = childNode.tagName === "tree" ? "list" : childNode.tagName;
const { arch: subViewArch, viewFields: subViewFields } = processArch(
xmlSerializer.serializeToString(childNode),
viewType,
coModel,
models
);
views[viewType] = { arch: subViewArch, fields: subViewFields };
node.removeChild(childNode);
}
viewFields[fieldName].views = views;
}
return false;
}
// list: extract groupby informations
if (viewType === "list" && node.tagName === "groupby" && node !== archDoc) {
const fieldName = node.getAttribute("name");
const coModel = models[resModel][fieldName].relation;
const { arch: subViewArch, viewFields: subViewFields } = processArch(
xmlSerializer.serializeToString(node),
"groupby",
coModel,
models
);
const groupbyView = { arch: subViewArch, fields: subViewFields };
viewFields[fieldName] = viewFields[fieldName] || {
...models[resModel][fieldName],
views: {},
};
viewFields[fieldName].views.groupby = groupbyView;
for (const child of [...node.children]) {
node.removeChild(child);
}
return false;
}
return true;
});
return { arch: xmlSerializer.serializeToString(archDoc), viewFields };
}
@@ -1,8 +1,6 @@
/** @odoo-module **/
import { deepCopy } from "@web/core/utils/objects";
import { registry } from "@web/core/registry";
import { generateLegacyLoadViewsResult } from "@web/legacy/legacy_load_views";
import { UPDATE_METHODS } from "@web/core/orm_service";
/**
@@ -90,9 +88,7 @@ export const viewService = {
.call(resModel, "get_views", [], { context, views, options: loadViewsOptions })
.then((result) => {
const { models, views } = result;
const modelsCopy = deepCopy(models); // for legacy views
const viewDescriptions = {
__legacy__: generateLegacyLoadViewsResult(resModel, views, modelsCopy),
fields: models[resModel],
relatedModels: models,
views: {},
@@ -42,7 +42,6 @@ const DebouncedField = basic_fields.DebouncedField;
* @param {Bus} [params.bus]
* @param {Object} [params.env]
* @param {Bus} [params.env.bus]
* @param {Object} [params.env.dataManager]
* @param {Object} [params.env.services]
* @param {Object} [params.services]
* @param {Object} [params.session]
@@ -62,40 +61,6 @@ async function _getMockedOwlEnv(params, mockServer) {
fetch: (resource, init) => mockServer.performFetch(resource, init),
}, params.env.browser),
bus: params.bus || params.env.bus || new Bus(),
dataManager: Object.assign({
load_action: (actionID, context) => {
return mockServer.performRpc('/web/action/load', {
action_id: actionID,
additional_context: context,
});
},
load_views: (params, options) => {
return mockServer.performRpc('/web/dataset/call_kw/' + params.model, {
args: [],
kwargs: {
context: params.context,
options: options,
views: params.views_descr,
},
method: 'get_views',
model: params.model,
}).then(function (views) {
views = Object.values(views).map((viewParams) => {
return getView(mockServer, viewParams);
});
if (favoriteFilters && 'search' in views) {
views.search.favoriteFilters = favoriteFilters;
}
return views;
});
},
load_filters: params => {
if (debug) {
console.log('[mock] load_filters', params);
}
return Promise.resolve([]);
},
}, params.env.dataManager),
services: Object.assign(services, params.env.services),
session: params.env.session || params.session || {},
});
@@ -495,30 +460,9 @@ async function addMockEnvironment(widget, params) {
ev.data.callback(result);
}
});
intercept(widget, 'load_action', async ev => {
const action = await env.dataManager.load_action(ev.data.actionID, ev.data.context);
ev.data.on_success(action);
});
intercept(widget, "load_views", async ev => {
const params = {
model: ev.data.modelName,
context: ev.data.context,
views_descr: ev.data.views,
};
const views = await env.dataManager.load_views(params, ev.data.options);
if ('search' in views && params.favoriteFilters) {
views.search.favoriteFilters = params.favoriteFilters;
}
ev.data.on_success(views);
});
intercept(widget, "get_session", ev => {
ev.data.callback(session);
});
intercept(widget, "load_filters", async ev => {
const filters = await env.dataManager.load_filters(ev.data);
ev.data.on_success(filters);
});
// make sure all other Odoo events bubbling up are intercepted
Object.keys(params.intercepts || {}).forEach(function (name) {
intercept(widget, name, params.intercepts[name]);
+1 -44
View File
@@ -12,7 +12,6 @@ import {
makeLegacySessionService,
} from "@web/legacy/utils";
import { makeLegacyActionManagerService } from "@web/legacy/backend_utils";
import { generateLegacyLoadViewsResult } from "@web/legacy/legacy_load_views";
import { viewService } from "@web/views/view_service";
import { actionService } from "@web/webclient/actions/action_service";
import { effectService } from "@web/core/effects/effect_service";
@@ -112,48 +111,6 @@ export function setupWebClientRegistries() {
*/
export async function addLegacyMockEnvironment(env, legacyParams = {}) {
// setup a legacy env
const dataManager = Object.assign(
{
load_action: (actionID, context) => {
return env.services.rpc("/web/action/load", {
action_id: actionID,
additional_context: context,
});
},
load_views: async (params, options) => {
let result = await env.services.rpc(`/web/dataset/call_kw/${params.model}`, {
args: [],
kwargs: {
context: params.context,
options: options,
views: params.views_descr,
},
method: "get_views",
model: params.model,
});
const { models, views: _views } = result;
result = generateLegacyLoadViewsResult(params.model, _views, models);
const views = result.fields_views;
for (const [, viewType] of params.views_descr) {
const fvg = views[viewType];
fvg.viewFields = fvg.fields;
fvg.fields = result.fields;
}
if (params.favoriteFilters && "search" in views) {
views.search.favoriteFilters = params.favoriteFilters;
}
return views;
},
load_filters: (params) => {
if (QUnit.config.debug) {
console.log("[mock] load_filters", params);
}
return Promise.resolve([]);
},
},
legacyParams.dataManager
);
let localSession;
if (legacyParams && legacyParams.getTZOffset) {
patchWithCleanup(session, {
@@ -162,7 +119,7 @@ export async function addLegacyMockEnvironment(env, legacyParams = {}) {
localSession = { getTZOffset: legacyParams.getTZOffset };
}
const baseEnv = { dataManager, bus: core.bus, session: localSession };
const baseEnv = { bus: core.bus, session: localSession };
const legacyEnv = makeTestEnvironment(Object.assign(baseEnv, legacyParams.env));
if (legacyParams.serviceRegistry) {
@@ -2976,19 +2976,6 @@ export class Wysiwyg extends Component {
if (payload.callback) {
payload.callback(this.env.session);
}
} else if (evType === 'load_views') {
const params = {
model: payload.modelName,
context: payload.context,
views_descr: payload.views,
};
this.env.dataManager
.load_views(params, payload.options || {})
.then(payload.on_success);
} else if (evType === 'load_filters') {
return this.env.dataManager
.load_filters(payload)
.then(payload.on_success);
}
}
_callService(payload) {