Merge PR #2312 into 14.0

Signed-off-by simahawk
pull/2689/head
OCA-git-bot 2023-11-27 14:21:06 +00:00
commit 6da6fe1878
14 changed files with 272 additions and 0 deletions

View File

@ -0,0 +1 @@
../../../../web_fix_modules_load

View File

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

View File

@ -0,0 +1 @@
bot, please!

View File

@ -0,0 +1,2 @@
from . import controllers
from . import models

View File

@ -0,0 +1,19 @@
# Copyright 2022 Camptocamp SA
# @author Simone Orsi <simahawk@gmail.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Fix translation loading",
"summary": "Fix translations loading from frontend with many modules",
"version": "14.0.1.0.0",
"category": "Hidden",
"license": "AGPL-3",
"development_status": "Alpha",
"website": "https://github.com/OCA/web",
"author": "Camptocamp,Odoo Community Association (OCA)",
"maintainers": ["simahawk"],
"depends": [
"web",
],
"data": ["templates/assets.xml"],
"installable": True,
}

View File

@ -0,0 +1 @@
from . import main

View File

@ -0,0 +1,48 @@
# Copyright 2022 Camptocamp SA
# @author Simone Orsi <simahawk@gmail.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import http
from odoo.addons.web.controllers.main import WebClient
class WebClientPatched(WebClient):
"""Handle conversions of modules' ids to their names."""
def _get_mod_names(self, mods):
"""Retrieve module names from their IDs in any form."""
mod_names = mods
model = http.request.env["ir.module.module"].sudo()
if isinstance(mods, str):
mods = [int(x.strip()) for x in mods.split(",") if x.strip().isdigit()]
if mods and isinstance(mods[0], int):
mod_names = model.browse(mods).mapped("name")
return mod_names
@http.route()
def qweb(self, unique, mods=None, db=None):
# Here `mods` comes as 1,2,3,4 string
mods = self._get_mod_names(mods)
return super().qweb(unique, mods=mods, db=db)
@http.route()
def bootstrap_translations(self, mods):
mods = self._get_mod_names(mods)
return super().bootstrap_translations(mods)
@http.route()
def csslist(self, mods=None):
mods = self._get_mod_names(mods)
return super().csslist(mods=mods)
@http.route()
def jslist(self, mods=None):
mods = self._get_mod_names(mods)
return super().jslist(mods=mods)
@http.route()
def translations(self, unique, mods=None, lang=None):
mods = self._get_mod_names(mods)
mods = ",".join(mods)
return super().translations(unique, mods, lang)

View File

@ -0,0 +1,2 @@
from . import ir_module
from . import ir_translation

View File

@ -0,0 +1,20 @@
# Copyright 2022 Camptocamp SA
# @author Simone Orsi <simahawk@gmail.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import json
from odoo import models
from odoo.addons.web.controllers.main import module_boot
class IrModule(models.Model):
_inherit = "ir.module.module"
def _session_modules_info(self):
"""Load modules info and return their mapping."""
module_names = module_boot(self.env.cr.dbname)
modules = self.sudo().search([("name", "in", module_names)])
data = {mod.name: {"id": mod.id} for mod in modules}
return json.dumps(data)

View File

@ -0,0 +1,17 @@
# Copyright 2022 Camptocamp SA
# @author Simone Orsi <simahawk@gmail.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models
class IrTranslation(models.Model):
_inherit = "ir.translation"
def get_translations_for_webclient(self, mods, lang):
# Intercept call to load translations from modules' ids instead of names.
if mods and isinstance(mods[0], int):
model = self.env["ir.module.module"].sudo()
mods = model.browse(mods).mapped("name")
return super().get_translations_for_webclient(mods, lang)

View File

@ -0,0 +1 @@
* Simone Orsi <simone.orsi@camptocamp.com>

View File

@ -0,0 +1,7 @@
Odoo loads translations and module info using their names.
When you have a lot of modules installed (eg: 500+)
this can lead to very big GET requests (more than 12k) which, in most of the cases,
will be blocked by the web server (eg: nginx) because they are too big.
This module tries to fix this by using modules' ids instead of names
reducing dramatically the size of such requests.

View File

@ -0,0 +1,127 @@
odoo.define("web_load_translations_fix.Session", function (require) {
"use strict";
var Session = require("web.Session");
var core = require("web.core");
var _t = core._t;
/**
* Override session manager to change how modules' are loaded: use ids instead of names.
*
*/
Session.include({
_modules_info: function () {
return odoo._modules_info;
},
_module_ids: function (names) {
const mod_names = names ? names : this.module_list;
const info = this._modules_info();
const ids = [];
_.each(mod_names, function (name) {
if (info[name]) {
ids.push(info[name].id);
}
});
console.debug("Session: load module names", mod_names);
return ids;
},
/**
* Full override due to no available hook.
* The whole code is taken as-is from Odoo core (comments included).
* Only the call to `load_translations` has been modified to use `_module_ids`.
* @returns: Promise
*/
load_translations: function () {
var lang = this.user_context.lang;
/* We need to get the website lang at this level.
The only way is to get it is to take the HTML tag lang
Without it, we will always send undefined if there is no lang
in the user_context. */
var html = document.documentElement,
htmlLang = html.getAttribute("lang");
if (!this.user_context.lang && htmlLang) {
lang = htmlLang.replace("-", "_");
}
return _t.database.load_translations(
this,
this._module_ids(),
lang,
this.translationURL
);
},
/**
* Full override due to no available hook.
* The whole code is taken as-is from Odoo core (comments included).
* Only the call to `load_qweb` and `bootstrap_translations` have been modified to use `_module_ids`.
* @returns: Promise
*/
session_init: function () {
var self = this;
var prom = this.session_reload();
if (this.is_frontend) {
return prom.then(function () {
return self.load_translations();
});
}
return prom.then(function () {
var promise = self.load_qweb(self._module_ids().join(","));
if (self.session_is_valid()) {
return promise.then(function () {
return self.load_modules();
});
}
return Promise.all([
promise,
self
.rpc("/web/webclient/bootstrap_translations", {
mods: self._module_ids(),
})
.then(function (trans) {
_t.database.set_bundle(trans);
}),
]);
});
},
/**
* Full override due to no available hook.
* The whole code is taken as-is from Odoo core (comments included).
* Only the call to `csslist` and `jslist` have been modified to use `_module_ids`.
* @returns: Promise
*/
load_modules: function () {
var self = this;
var modules = odoo._modules;
var all_modules = _.uniq(self.module_list.concat(modules));
var to_load = _.difference(modules, self.module_list).join(",");
this.module_list = all_modules;
var loaded = Promise.resolve(self.load_translations());
var locale = "/web/webclient/locale/" + self.user_context.lang || "en_US";
var file_list = [locale];
if (to_load.length) {
loaded = Promise.all([
loaded,
self
.rpc("/web/webclient/csslist", {
mods: self._module_ids(to_load),
})
.then(self.load_css.bind(self)),
self.load_qweb(to_load),
self
.rpc("/web/webclient/jslist", {mods: self._module_ids(to_load)})
.then(function (files) {
file_list = file_list.concat(files);
}),
]);
}
return loaded
.then(function () {
return self.load_js(file_list);
})
.then(function () {
self._configureLocale();
});
},
});
});

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr="//t[@t-raw='get_modules_order()']/.." position="after">
<script type="text/javascript" charset="utf-8">
odoo._modules_info = <t
t-raw="request.env['ir.module.module']._session_modules_info()"
/>;
</script>
</xpath>
</template>
<template id="assets_common" inherit_id="web.assets_common">
<xpath expr="//script[last()]" position="after">
<script src="/web_fix_modules_load/static/src/js/session.js" />
</xpath>
</template>
</odoo>