Skip to content

Commit

Permalink
[IMP] website, *: allow to add a language from theme tab + options
Browse files Browse the repository at this point in the history
*: web_editor, portal

This adds a button to add language in theme tab. When clicked, opens
the "add a language" wizard.
If the website has only one language, the options to enable language
in header & footer are hidden.
When an user add a language, the default behavior is Header: dropdown
& Footer: inline.
Also provides two more rendering options : Language Code and Flag +
Language Code.

task-3457554

closes odoo#119650

Related: odoo/design-themes#651
Related: odoo/upgrade#5214
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
Co-authored-by: qsm-odoo <qsm@odoo.com>
  • Loading branch information
SergeBayet and qsm-odoo committed Oct 25, 2023
1 parent fb63a6a commit dc81acf
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 78 deletions.
5 changes: 4 additions & 1 deletion addons/portal/views/portal_templates.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<template id="language_selector" name="Language Selector">
<t t-nocache="The query strings can change for the same page and the same rendering."
t-nocache-no_text="no_text"
t-nocache-codes="codes"
t-nocache-_div_classes="_div_classes"
t-nocache-_btn_class="_btn_class"
t-nocache-_txt_class="_txt_class"
Expand All @@ -82,13 +83,15 @@
<span t-if="not no_text"
t-attf-class="align-middle #{_txt_class}"
t-esc="active_lang[2].split('/').pop()"/>
<span t-elif="codes" class="align-middle" t-esc="active_lang[1].split('_').pop(0).upper()"/>
</button>
<div t-attf-class="dropdown-menu #{_dropdown_menu_class}" role="menu">
<t t-foreach="languages" t-as="lg">
<a class="dropdown-item" t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang_code=lg[0])"
t-attf-class="dropdown-item js_change_lang #{active_lang == lg and 'active'}"
t-att-data-url_code="lg[1]" t-att-title="lg[2].split('/').pop()">
<span t-if="not no_text" t-out="lg[2].split('/').pop()" t-attf-class="#{_txt_class}"/>
<span t-if="not no_text" t-esc="lg[2].split('/').pop()" t-attf-class="#{_txt_class}"/>
<span t-elif="codes" t-esc="lg[1].split('_').pop(0).upper()" t-attf-class="align-middle #{_txt_class}"/>
</a>
</t>
</div>
Expand Down
2 changes: 1 addition & 1 deletion addons/web_editor/static/src/js/editor/snippets.editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
},

//--------------------------------------------------------------------------
Expand Down
61 changes: 35 additions & 26 deletions addons/web_editor/static/src/js/editor/snippets.options.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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, {
Expand All @@ -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',
Expand All @@ -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);
},
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -9209,6 +9218,6 @@ export default {
// Other names for convenience
Class: SnippetOptionWidget,
registry: registry,

clearM2oRpcCache,
serviceCached,
clearServiceCache,
};
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down
89 changes: 88 additions & 1 deletion addons/website/static/src/js/editor/snippets.options.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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({
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions addons/website/static/src/scss/website.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
11 changes: 6 additions & 5 deletions addons/website/static/tests/tours/automatic_editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ import wTourUtils from "@website/js/tours/tour_utils";

wTourUtils.registerWebsitePreviewTour('automatic_editor_on_new_website', {
test: true,
edition: true,
url: '/',
},
() => [
wTourUtils.goToTheme(),
{
content: "Select the language dropdown",
trigger: 'iframe .js_language_selector .dropdown-toggle'
content: "click on Add a language",
trigger: "we-button[data-add-language]"
},
{
content: "click on Add a language",
trigger: 'iframe a.o_add_language',
content: "confirm leave editor",
trigger: ".modal-dialog button.btn-primary"
},
{
content: "type Parseltongue",
Expand All @@ -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",
Expand Down
13 changes: 8 additions & 5 deletions addons/website/static/tests/tours/rte.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
15 changes: 12 additions & 3 deletions addons/website/views/snippets/snippets.xml
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,8 @@
data-page-option-name="header_text_color"/>
</div>

<div data-selector="#wrapwrap > header"
<div data-js="HeaderElements"
data-selector="#wrapwrap > header"
data-no-check="true"
groups="website.group_website_designer">
<we-row string="Elements" class="o_we_full_row align-items-start">
Expand All @@ -997,6 +998,7 @@
data-reset-view-arch="true"
data-reload="/"/>
<we-button title="Show/hide language selector" class="fa fa-flag d-flex justify-content-center flex-grow-1"
data-name="header_language_selector_opt"
data-customize-website-views="website.header_language_selector"
data-reset-view-arch="true"
data-reload="/"/>
Expand Down Expand Up @@ -1107,6 +1109,8 @@
<we-button data-customize-website-views="">Text</we-button>
<we-button data-customize-website-views="website.header_language_selector_flag, website.header_language_selector_no_text">Flag</we-button>
<we-button data-customize-website-views="website.header_language_selector_flag">Flag and Text</we-button>
<we-button data-customize-website-views="website.header_language_selector_code, website.header_language_selector_no_text">Code</we-button>
<we-button data-customize-website-views="website.header_language_selector_flag, website.header_language_selector_code, website.header_language_selector_no_text">Flag and Code</we-button>
</we-select>
</div>

Expand Down Expand Up @@ -1259,16 +1263,18 @@
data-null-value="'NULL'"
data-with-combinations="customizeWebsiteColor"
data-with-gradients="true"/>
<we-select string="Language Selector" data-reload="/">
<we-select data-name="footer_language_selector_opt" string="Language Selector" data-reload="/">
<we-button data-name="language_selector_none_opt"
data-customize-website-views="">None</we-button>
<we-button data-customize-website-views="portal.footer_language_selector">Dropdown</we-button>
<we-button data-customize-website-views="portal.footer_language_selector, website.footer_language_selector_inline">Inline</we-button>
</we-select>
<we-select string="Label" class="o_we_sublevel_1" data-dependencies="!language_selector_none_opt" data-reload="/">
<we-select data-name="footer_language_selector_label_opt" string="Label" class="o_we_sublevel_1" data-dependencies="!language_selector_none_opt" data-reload="/">
<we-button data-customize-website-views="">Text</we-button>
<we-button data-customize-website-views="website.footer_language_selector_flag, website.footer_language_selector_no_text">Flag</we-button>
<we-button data-customize-website-views="website.footer_language_selector_flag">Flag and Text</we-button>
<we-button data-customize-website-views="website.footer_language_selector_code, website.footer_language_selector_no_text">Code</we-button>
<we-button data-customize-website-views="website.footer_language_selector_flag, website.footer_language_selector_code, website.footer_language_selector_no_text">Flag and Code</we-button>
</we-select>
</div>

Expand Down Expand Up @@ -1690,6 +1696,9 @@
<we-row string="Theme">
<we-button data-switch-theme="" data-no-preview="true" class="o_we_bg_brand_primary">Switch Theme</we-button>
</we-row>
<we-row string="Language">
<we-button data-add-language="" data-no-preview="true" class="o_we_bg_brand_primary">Add a Language</we-button>
</we-row>
<we-select string="Page Layout" data-variable="layout">
<we-button data-customize-website-variable="'full'" data-name="layout_full_opt">Full</we-button>
<we-button data-customize-website-variable="'boxed'">Boxed</we-button>
Expand Down
Loading

0 comments on commit dc81acf

Please sign in to comment.