+
diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index 90143886c7e..8ff6e0b6df8 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -2109,7 +2109,7 @@ var SnippetsMenu = Widget.extend({
this.el.ownerDocument.body.classList.remove('editor_has_snippets');
// Dispose BS tooltips.
this.tooltips.dispose();
- options.clearM2oRpcCache();
+ options.clearServiceCache();
},
//--------------------------------------------------------------------------
diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js
index d5d01a7b237..c7fa1d15c8e 100644
--- a/addons/web_editor/static/src/js/editor/snippets.options.js
+++ b/addons/web_editor/static/src/js/editor/snippets.options.js
@@ -45,7 +45,38 @@ import { jsonrpc } from "@web/core/network/rpc_service";
const preserveCursor = OdooEditorLib.preserveCursor;
const { DateTime } = luxon;
const resetOuids = OdooEditorLib.resetOuids;
-
+let _serviceCache = {
+ orm: {},
+ rpc: {},
+};
+const clearServiceCache = () => {
+ _serviceCache = {
+ orm: {},
+ rpc: {},
+ };
+};
+/**
+ * Caches rpc/orm service
+ * @param {Function} service
+ * @param {...any} args
+ * @returns
+ */
+function serviceCached(service) {
+ const cache = _serviceCache;
+ return Object.assign(Object.create(service), {
+ call() {
+ const serviceName = Object.prototype.hasOwnProperty.call(service, "call")
+ ? "orm"
+ : "rpc";
+ const cacheId = JSON.stringify(arguments);
+ if (!cache[serviceName][cacheId]) {
+ cache[serviceName][cacheId] =
+ serviceName == "rpc" ? service(...arguments) : service.call(...arguments);
+ }
+ return cache[serviceName][cacheId];
+ },
+ });
+}
/**
* @param {HTMLElement} el
* @param {string} [title]
@@ -2596,10 +2627,6 @@ const SelectPagerUserValueWidget = SelectUserValueWidget.extend({
}
});
-let m2oRpcCache = {};
-const clearM2oRpcCache = () => {
- m2oRpcCache = {};
-};
const Many2oneUserValueWidget = SelectUserValueWidget.extend({
className: (SelectUserValueWidget.prototype.className || '') + ' o_we_many2one',
events: Object.assign({}, SelectUserValueWidget.prototype.events, {
@@ -2621,7 +2648,6 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({
init(parent, title, options, $target) {
this.afterSearch = [];
this.displayNameCache = {};
- this._rpcCache = m2oRpcCache;
const {dataAttributes} = options;
Object.assign(options, {
limit: '5',
@@ -2645,7 +2671,7 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({
options.nullText = $target[0].dataset.nullText ||
JSON.parse($target[0].dataset.oeContactOptions || '{}')['null_text'];
- this.orm = this._withCache(this.bindService("orm"));
+ this.orm = serviceCached(this.bindService("orm"));
return this._super(...arguments);
},
@@ -2773,23 +2799,6 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({
// Private
//--------------------------------------------------------------------------
- /**
- * Caches the rpc.
- *
- * @override
- */
- _withCache(orm) {
- const cache = this._rpcCache;
- return Object.assign(Object.create(orm), {
- call() {
- const cacheId = JSON.stringify(arguments);
- if (!cache[cacheId]) {
- cache[cacheId] = orm.call(...arguments);
- }
- return cache[cacheId];
- },
- });
- },
/**
* Searches the database for corresponding records and updates the dropdown
*
@@ -9209,6 +9218,6 @@ export default {
// Other names for convenience
Class: SnippetOptionWidget,
registry: registry,
-
- clearM2oRpcCache,
+ serviceCached,
+ clearServiceCache,
};
diff --git a/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js b/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js
index b3d20143897..882203cd7be 100644
--- a/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js
+++ b/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js
@@ -988,7 +988,7 @@ export class WysiwygAdapterComponent extends Wysiwyg {
} else if (event.data.action) {
callback = () => {
this.leaveEditMode({
- onLeave: () => this.action.doAction(event.data.action),
+ onLeave: () => this.action.doAction(event.data.action, event.data.options || {}),
forceLeave: true,
reloadIframe: false,
});
diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js
index 36727c4b503..3153ceb9364 100644
--- a/addons/website/static/src/js/editor/snippets.options.js
+++ b/addons/website/static/src/js/editor/snippets.options.js
@@ -1406,6 +1406,39 @@ options.registry.OptionsTab = options.Class.extend({
action: 'website.theme_install_kanban_action',
});
},
+ /**
+ * @see this.selectClass for parameters
+ */
+ async addLanguage(previewMode, widgetValue, params) {
+ // Retrieve the website id to check by default the website checkbox in
+ // the dialog box 'action_view_base_language_install'
+ const websiteId = this.options.context.website_id;
+ const save = await new Promise((resolve) => {
+ Dialog.confirm(
+ this,
+ _t("Adding a language requires to leave the editor. This will save all your changes, are you sure you want to proceed?"),
+ {
+ confirm_callback: () => resolve(true),
+ cancel_callback: () => resolve(false),
+ }
+ );
+ });
+ if (!save) {
+ return;
+ }
+ this.trigger_up("request_save", {
+ reload: false,
+ action: "base.action_view_base_language_install",
+ options: {
+ additionalContext: {
+ params: {
+ website_id: websiteId,
+ url_return: "[lang]",
+ }
+ },
+ }
+ });
+ },
/**
* @see this.selectClass for parameters
*/
@@ -2191,7 +2224,13 @@ options.registry.collapse = options.Class.extend({
options.registry.WebsiteLevelColor = options.Class.extend({
specialCheckAndReloadMethodsNames: options.Class.prototype.specialCheckAndReloadMethodsNames
.concat(['customizeWebsiteLayer2Color']),
-
+ /**
+ * @constructor
+ */
+ init() {
+ this._super(...arguments);
+ this._rpc = options.serviceCached(this.bindService("rpc"));
+ },
/**
* @see this.selectClass for parameters
*/
@@ -2234,6 +2273,24 @@ options.registry.WebsiteLevelColor = options.Class.extend({
}
return this._super(...arguments);
},
+ /**
+ * @override
+ */
+ async _computeWidgetVisibility(widgetName, params) {
+ const _super = this._super.bind(this);
+ if (
+ [
+ "footer_language_selector_label_opt",
+ "footer_language_selector_opt",
+ ].includes(widgetName)
+ ) {
+ this._languages = await this._rpc.call("/website/get_languages");
+ if (this._languages.length === 1) {
+ return false;
+ }
+ }
+ return _super(...arguments);
+ },
});
options.registry.HeaderLayout = options.registry.WebsiteLevelColor.extend({
@@ -2259,6 +2316,36 @@ options.registry.HeaderLayout = options.registry.WebsiteLevelColor.extend({
}
});
+options.registry.HeaderElements = options.Class.extend({
+ /**
+ * @constructor
+ */
+ init() {
+ this._super(...arguments);
+ this._rpc = options.serviceCached(this.bindService("rpc"));
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ async _computeWidgetVisibility(widgetName, params) {
+ const _super = this._super.bind(this);
+ switch (widgetName) {
+ case "header_language_selector_opt":
+ this._languages = await this._rpc.call("/website/get_languages");
+ if (this._languages.length === 1) {
+ return false;
+ }
+ break;
+ }
+ return _super(...arguments);
+ },
+});
+
options.registry.HeaderNavbar = options.Class.extend({
/**
* Particular case: we want the option to be associated on the header navbar
diff --git a/addons/website/static/src/scss/website.scss b/addons/website/static/src/scss/website.scss
index 2d942c05b19..c2262353c92 100644
--- a/addons/website/static/src/scss/website.scss
+++ b/addons/website/static/src/scss/website.scss
@@ -1452,6 +1452,7 @@ header {
margin-right: 0.2em;
border-radius: $border-radius-pill;
object-fit: cover;
+ box-shadow: rgba(0, 0, 0, 0.429) 1px 1px 1px;
}
span.list-inline-item.o_add_language:last-child {
display: none !important; // Hide the separator if it is the last list item
diff --git a/addons/website/static/src/systray_items/website_switcher.js b/addons/website/static/src/systray_items/website_switcher.js
index 83dac737174..dff26ad0aea 100644
--- a/addons/website/static/src/systray_items/website_switcher.js
+++ b/addons/website/static/src/systray_items/website_switcher.js
@@ -27,7 +27,7 @@ export class WebsiteSwitcherSystray extends Component {
const path = pathname + search + hash;
window.location.href = `${encodeURI(website.domain)}/web#action=website.website_preview&path=${encodeURIComponent(path)}&website_id=${encodeURIComponent(website.id)}`;
} else {
- this.websiteService.goToWebsite({ websiteId: website.id });
+ this.websiteService.goToWebsite({ websiteId: website.id, path: "", lang: "default" });
if (!website.domain) {
const closeFn = this.notificationService.add(
_t(
diff --git a/addons/website/static/tests/tours/automatic_editor.js b/addons/website/static/tests/tours/automatic_editor.js
index 8ddf948df14..4153c368061 100644
--- a/addons/website/static/tests/tours/automatic_editor.js
+++ b/addons/website/static/tests/tours/automatic_editor.js
@@ -4,16 +4,18 @@ import wTourUtils from "@website/js/tours/tour_utils";
wTourUtils.registerWebsitePreviewTour('automatic_editor_on_new_website', {
test: true,
+ edition: true,
url: '/',
},
() => [
- {
- content: "Select the language dropdown",
- trigger: 'iframe .js_language_selector .dropdown-toggle'
- },
+ wTourUtils.goToTheme(),
{
content: "click on Add a language",
- trigger: 'iframe a.o_add_language',
+ trigger: "we-button[data-add-language]"
+ },
+ {
+ content: "confirm leave editor",
+ trigger: ".modal-dialog button.btn-primary"
},
{
content: "type Parseltongue",
@@ -37,7 +39,6 @@ wTourUtils.registerWebsitePreviewTour('automatic_editor_on_new_website', {
{
content: "Select parseltongue",
trigger: 'iframe a.js_change_lang[data-url_code=pa_GB]',
- extra_trigger: 'iframe a.js_change_lang .o_lang_flag',
},
{
content: "Check that we're on parseltongue and then go to settings",
diff --git a/addons/website/static/tests/tours/rte.js b/addons/website/static/tests/tours/rte.js
index ade431016cb..30a1f3a28b3 100644
--- a/addons/website/static/tests/tours/rte.js
+++ b/addons/website/static/tests/tours/rte.js
@@ -6,13 +6,16 @@ import { whenReady } from "@odoo/owl";
wTourUtils.registerWebsitePreviewTour('rte_translator', {
test: true,
url: '/',
+ edition: true,
wait_for: whenReady(),
-}, () => [{
- content: "click language dropdown",
- trigger: 'iframe .js_language_selector .dropdown-toggle',
-}, {
+}, () => [
+wTourUtils.goToTheme(),
+{
content: "click on Add a language",
- trigger: 'iframe a.o_add_language',
+ trigger: "we-button[data-add-language]"
+}, {
+ content: "confirm leave editor",
+ trigger: ".modal-dialog button.btn-primary"
}, {
content: "type Parseltongue",
trigger: 'div[name="lang_ids"] .o_input_dropdown input',
diff --git a/addons/website/views/snippets/snippets.xml b/addons/website/views/snippets/snippets.xml
index 14fde7ddfb9..085b0457626 100644
--- a/addons/website/views/snippets/snippets.xml
+++ b/addons/website/views/snippets/snippets.xml
@@ -987,7 +987,8 @@
data-page-option-name="header_text_color"/>
-
@@ -997,6 +998,7 @@
data-reset-view-arch="true"
data-reload="/"/>
@@ -1107,6 +1109,8 @@
Text
Flag
Flag and Text
+ Code
+ Flag and Code
@@ -1259,16 +1263,18 @@
data-null-value="'NULL'"
data-with-combinations="customizeWebsiteColor"
data-with-gradients="true"/>
-
+
None
Dropdown
Inline
-
+
Text
Flag
Flag and Text
+ Code
+ Flag and Code
@@ -1690,6 +1696,9 @@
Switch Theme
+
+ Add a Language
+
Full
Boxed
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index 64bd7b24998..6104ed42b46 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -2078,17 +2078,6 @@
-