[FIX] website, *: fix the website custom systray and menus in mobile

*: website_event, website_links

This commit fixes the navbar on the enterprise mobile mode. With the
enterprise modules, in mobile mode, the navbar menus are removed and
displayed in an an additional systray item, the BurgerMenu.

This behavior was not adapted to the website systray and contextual
custom menus, introduced in [1].

For the website systray, some elements are hidden in mobile:
- the "edit" and "translate" buttons,
- the mobile preview,
- the "+ new" new content button,

The logic for displaying the contextual custom menus is moved from the
navbar patch to a service, so that we can define a patch for the
BurgerMenu that uses that logic and has the same behavior as the
navbar.
For now, the "Menu Editor" and the "HTML/CSS Editor" menus are hidden in
mobile.

[1]: https://github.com/odoo/odoo/commit/31cc10b91dc7762e23b4bde9b945be0c4ce3fe3b

task-2687506

closes odoo/odoo#99605

Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
Younn Olivier
2022-09-12 17:33:11 +02:00
committed by qsm-odoo
parent a8361c8fcd
commit 78c59afd13
18 changed files with 231 additions and 162 deletions
+2
View File
@@ -241,6 +241,8 @@
'website/static/src/components/dialog/*.scss',
'website/static/src/components/editor/editor.js',
'website/static/src/components/editor/editor.scss',
'website/static/src/components/navbar/navbar.js',
'website/static/src/components/burger_menu/burger_menu.js',
'website/static/src/components/switch/switch.js',
'website/static/src/components/switch/switch.scss',
'website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js',
@@ -0,0 +1,49 @@
/** @odoo-module **/
import { BurgerMenu } from '@web/webclient/burger_menu/burger_menu';
import { useService } from '@web/core/utils/hooks';
import { registry } from "@web/core/registry";
import { patch } from 'web.utils';
const websiteSystrayRegistry = registry.category('website_systray');
patch(BurgerMenu.prototype, 'website_burger_menu', {
setup() {
this._super();
this.websiteCustomMenus = useService('website_custom_menus');
if (!websiteSystrayRegistry.contains('burger_menu')) {
websiteSystrayRegistry.add('burger_menu', registry.category('systray').get('burger_menu'), {sequence: 0});
}
},
/**
* @override
*/
get currentAppSections() {
const currentAppSections = this._super();
if (this.currentApp && this.currentApp.xmlid === 'website.menu_website_configuration') {
return this.websiteCustomMenus.addCustomMenus(currentAppSections).filter(section => section.childrenTree.length);
}
return currentAppSections;
},
/**
* This dummy setter is only here to prevent conflicts between the
* Enterprise BurgerMenue extension and the Website BurgerMenu patch.
*/
set currentAppSections(_) {},
/**
* @override
*/
async _onMenuClicked(menu) {
const websiteMenu = this.websiteCustomMenus.get(menu.xmlid);
if (websiteMenu) {
this.websiteCustomMenus.open(menu.xmlid);
this._closeBurger();
} else {
this._super(menu);
}
},
});
@@ -0,0 +1,64 @@
/** @odoo-module **/
import { NavBar } from '@web/webclient/navbar/navbar';
import { useService, useBus } from '@web/core/utils/hooks';
import { registry } from "@web/core/registry";
import { patch } from 'web.utils';
const websiteSystrayRegistry = registry.category('website_systray');
patch(NavBar.prototype, 'website_navbar', {
setup() {
this._super();
this.websiteService = useService('website');
this.websiteCustomMenus = useService('website_custom_menus');
// The navbar is rerendered with an event, as it can not naturally be
// with props/state (the WebsitePreview client action and the navbar
// are not related).
useBus(websiteSystrayRegistry, 'EDIT-WEBSITE', () => this.render(true));
if (this.env.debug && !websiteSystrayRegistry.contains('web.debug_mode_menu')) {
websiteSystrayRegistry.add('web.debug_mode_menu', registry.category('systray').get('web.debug_mode_menu'), {sequence: 100});
}
useBus(websiteSystrayRegistry, 'CONTENT-UPDATED', () => this.render(true));
},
/**
* @override
*/
get systrayItems() {
if (this.websiteService.currentWebsite && this.websiteService.isRestrictedEditor) {
return websiteSystrayRegistry
.getEntries()
.map(([key, value], index) => ({ key, ...value, index }))
.filter((item) => ('isDisplayed' in item ? item.isDisplayed(this.env) : true))
.reverse();
}
return this._super();
},
/**
* @override
*/
get currentAppSections() {
const currentAppSections = this._super();
if (this.currentApp && this.currentApp.xmlid === 'website.menu_website_configuration') {
return this.websiteCustomMenus.addCustomMenus(currentAppSections).filter(section => section.childrenTree.length);
}
return currentAppSections;
},
/**
* @override
*/
onNavBarDropdownItemSelection(menu) {
const websiteMenu = this.websiteCustomMenus.get(menu.xmlid);
if (websiteMenu) {
return this.websiteCustomMenus.open(menu.xmlid);
}
return this._super(menu);
},
});
@@ -22,6 +22,6 @@ Switch.template = xml`
<label t-att-class="'o_switch' + extraClasses">
<input type="checkbox" t-att-checked="props.value" t-on-change="(ev) => props.onChange(ev.target.checked)"/>
<span/>
<div t-if="props.label"><t t-esc="props.label"/></div>
<span t-if="props.label" t-esc="props.label" class="ms-2"/>
</label>
`;
@@ -10,7 +10,6 @@ $o-we-switch-inactive-color: rgba($text-muted, 0.4) !default;
display: none;
+ span {
margin-right: 0.5em;
border-radius: $o-we-switch-size;
width: $o-we-switch-size * 1.7;
padding-left: 3px;
@@ -1,129 +1,84 @@
/** @odoo-module **/
import { NavBar } from '@web/webclient/navbar/navbar';
import { useService, useBus } from '@web/core/utils/hooks';
import { registry } from "@web/core/registry";
import { patch } from 'web.utils';
import { EditMenuDialog } from '@website/components/dialog/edit_menu';
import { OptimizeSEODialog } from '@website/components/dialog/seo';
import {PagePropertiesDialog} from '@website/components/dialog/page_properties';
const websiteSystrayRegistry = registry.category('website_systray');
const { useState } = owl;
patch(NavBar.prototype, 'website_navbar', {
setup() {
this._super();
this.orm = useService('orm');
this.websiteService = useService('website');
this.websiteContext = useState(this.websiteService.context);
// The navbar is rerendered with an event, as it can not naturally be
// with props/state (the WebsitePreview client action and the navbar
// are not related).
useBus(websiteSystrayRegistry, 'EDIT-WEBSITE', () => this.render(true));
if (this.env.debug && !websiteSystrayRegistry.contains('web.debug_mode_menu')) {
websiteSystrayRegistry.add('web.debug_mode_menu', registry.category('systray').get('web.debug_mode_menu'), {sequence: 100});
}
useBus(websiteSystrayRegistry, 'CONTENT-UPDATED', () => this.render(true));
this.websiteEditingMenus = {
'website.menu_edit_menu': {
Component: EditMenuDialog,
isDisplayed: () => !!this.websiteService.currentWebsite && this.websiteService.isDesigner,
/**
* This service displays contextual menus, depending of the state of the
* website. These menus are defined in xml with the "website_preview" action,
* which is overriden here for displaying dialogs, or regular components that
* are not client actions.
*/
export const websiteCustomMenus = {
dependencies: ['website', 'orm', 'dialog', 'ui'],
start(env, { website, orm, dialog, ui }) {
const services = { website, orm, dialog, ui };
return {
get(xmlId) {
return registry.category('website_custom_menus').get(xmlId, null);
},
'website.menu_optimize_seo': {
Component: OptimizeSEODialog,
isDisplayed: () => this.websiteService.currentWebsite && !!this.websiteService.currentWebsite.metadata.mainObject,
open(xmlId) {
const menu = this.get(xmlId);
if (menu.openWidget) {
return menu.openWidget(services);
}
return dialog.add(
menu.Component,
menu.getProps && menu.getProps(services),
);
},
'website.menu_current_page': {
isDisplayed: () => this.canShowAceEditor(),
},
'website.menu_ace_editor': {
openWidget: () => this.websiteContext.showAceEditor = true,
isDisplayed: () => this.canShowAceEditor(),
},
'website.menu_page_properties': {
Component: PagePropertiesDialog,
isDisplayed: () => this.canShowPageProperties(),
getProps: () => ({
onRecordSaved: (record) => {
return this.orm.read('website.page', [record.resId], ['url']).then(res => {
this.websiteService.goToWebsite({websiteId: record.data.website_id[0], path: res[0]['url']});
});
},
})
addCustomMenus(sections) {
const filteredSections = [];
for (const section of sections) {
const isWebsiteCustomMenu = !!this.get(section.xmlid);
const displayWebsiteCustomMenu = isWebsiteCustomMenu && website.isRestrictedEditor && this.get(section.xmlid).isDisplayed(env);
if (!isWebsiteCustomMenu || displayWebsiteCustomMenu) {
let subSections = [];
if (section.childrenTree.length) {
subSections = this.addCustomMenus(section.childrenTree);
}
filteredSections.push(Object.assign({}, section, {childrenTree: subSections}));
}
}
return filteredSections;
},
};
},
}
};
registry.category('services').add('website_custom_menus', websiteCustomMenus);
filterWebsiteMenus(sections) {
const filteredSections = [];
for (const section of sections) {
const isWebsiteCustomMenu = this.websiteEditingMenus[section.xmlid];
const displayWebsiteCustomMenu = isWebsiteCustomMenu && this.websiteService.isRestrictedEditor && this.websiteEditingMenus[section.xmlid].isDisplayed();
if (!isWebsiteCustomMenu || displayWebsiteCustomMenu) {
let subSections = [];
if (section.childrenTree.length) {
subSections = this.filterWebsiteMenus(section.childrenTree);
}
filteredSections.push(Object.assign({}, section, {childrenTree: subSections}));
}
}
return filteredSections;
},
/**
* @override
*/
get systrayItems() {
if (this.websiteService.currentWebsite && this.websiteService.isRestrictedEditor) {
return websiteSystrayRegistry
.getEntries()
.map(([key, value], index) => ({ key, ...value, index }))
.filter((item) => ('isDisplayed' in item ? item.isDisplayed(this.env) : true))
.reverse();
}
return this._super();
},
/**
* @override
*/
get currentAppSections() {
const currentAppSections = this._super();
if (this.currentApp && this.currentApp.xmlid === 'website.menu_website_configuration') {
return this.filterWebsiteMenus(currentAppSections).filter(section => section.childrenTree.length);
}
return currentAppSections;
},
/**
* @override
*/
onNavBarDropdownItemSelection(menu) {
const websiteMenu = this.websiteEditingMenus[menu.xmlid];
if (websiteMenu) {
return websiteMenu.openWidget ?
websiteMenu.openWidget() :
this.websiteService.openMenuDialog(
websiteMenu.Component,
websiteMenu.getProps && websiteMenu.getProps(),
);
}
return this._super(menu);
},
canShowPageProperties() {
return this.websiteService.currentWebsite
&& !!this.websiteService.currentWebsite.metadata.mainObject
&& this.websiteService.currentWebsite.metadata.mainObject.model === 'website.page';
},
canShowAceEditor() {
return this.websiteService.currentWebsite && this.websiteService.currentWebsite.metadata.viewXmlid
&& !this.websiteContext.showNewContentModal && !this.websiteContext.edition;
},
registry.category('website_custom_menus').add('website.menu_edit_menu', {
Component: EditMenuDialog,
isDisplayed: (env) => !!env.services.website.currentWebsite
&& env.services.website.isDesigner
&& !env.services.ui.isSmall,
});
registry.category('website_custom_menus').add('website.menu_optimize_seo', {
Component: OptimizeSEODialog,
isDisplayed: (env) => env.services.website.currentWebsite
&& !!env.services.website.currentWebsite.metadata.mainObject,
});
registry.category('website_custom_menus').add('website.menu_current_page', {
isDisplayed: (env) => !!env.services.website.currentWebsite,
},);
registry.category('website_custom_menus').add('website.menu_ace_editor', {
openWidget: (services) => services.website.context.showAceEditor = true,
isDisplayed: (env) => env.services.website.currentWebsite
&& env.services.website.currentWebsite.metadata.viewXmlid
&& !env.services.ui.isSmall,
});
registry.category('website_custom_menus').add('website.menu_page_properties', {
Component: PagePropertiesDialog,
isDisplayed: (env) => env.services.website.currentWebsite
&& !!env.services.website.currentWebsite.metadata.mainObject
&& env.services.website.currentWebsite.metadata.mainObject.model === 'website.page',
getProps: (services) => ({
onRecordSaved: (record) => {
return services.orm.read('website.page', [record.resId], ['url']).then(res => {
services.website.goToWebsite({websiteId: record.data.website_id[0], path: res[0]['url']});
});
},
})
});
@@ -163,9 +163,6 @@ export const websiteService = {
set actionJsId(jsId) {
actionJsId = jsId;
},
openMenuDialog(Component, props) {
return dialog.add(Component, props);
},
goToWebsite({ websiteId, path, edition, translation } = {}) {
action.doAction('website.website_preview', {
clearBreadcrumbs: true,
@@ -3,8 +3,8 @@
<t t-name="website.EditInBackendSystray" owl="1">
<div class="o_website_edit_in_backend">
<a href="#" accesskey="e" t-on-click="editInBackend">
<span class="fa fa-cog me-2"/>
<t t-esc="state.mainObjectName"/>
<span class="fa fa-cog"/>
<span class= "d-none d-md-block ms-2" t-esc="state.mainObjectName"/>
</a>
</div>
</t>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="website.EditWebsiteSystray" owl="1">
<div class="o_edit_website_container" t-att-class="{ o_translatable: translatable }">
<div class="o_edit_website_container d-none d-md-block" t-att-class="{ o_translatable: translatable }">
<a href="#" accesskey="a" t-on-click="startEdit">
<span t-if="translatable" class="fa fa-pencil me-2"/>
<span t-else="" class="fa me-2" t-att-class="{ 'fa-refresh fa-spin': this.state.isLoading, 'fa-pencil': !this.state.isLoading }"/>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="website.MobilePreviewSystray" owl="1">
<div class="o_mobile_preview o_menu_systray_item" t-on-click="() => this.websiteService.context.isMobile = !this.websiteService.context.isMobile">
<div class="o_mobile_preview o_menu_systray_item d-none d-md-block" t-on-click="() => this.websiteService.context.isMobile = !this.websiteService.context.isMobile">
<a href="#" accesskey="v"><span title="Mobile preview" role="img" aria-label="Mobile preview" class="fa fa-lg fa-mobile"/></a>
</div>
</t>
@@ -40,7 +40,7 @@
</t>
<t t-name="website.NewContentSystray" owl="1">
<div class="o_menu_systray_item o_new_content_container" t-on-click="onClick">
<div class="o_menu_systray_item o_new_content_container d-none d-md-block" t-on-click="onClick">
<a href="#" accesskey="c"><span class="fa fa-plus me-2"/>New</a>
<NewContentModal t-if="websiteContext.showNewContentModal"/>
</div>
@@ -37,7 +37,7 @@ PublishSystray.template = xml`
<div t-on-click="publishContent" class="o_menu_systray_item d-md-flex ms-auto" data-hotkey="p">
<a href="#">
<Switch value="state.published" extraClasses="'mb-0 o_switch_danger_success'"/>
<t t-esc="this.label"/>
<span class="d-none d-md-block ms-2" t-esc="this.label"/>
</a>
</div>`;
PublishSystray.components = {
@@ -2,7 +2,7 @@
<templates xml:space="preserve">
<t t-name="website.TranslateWebsiteSystray" owl="1">
<div class="o_translate_website_container">
<div class="o_translate_website_container d-none d-md-block">
<a href="#" accesskey="t" t-on-click="startTranslate">
<span t-if="websiteContext.translation" class="fa fa-refresh fa-spin me-2"/>
TRANSLATE
@@ -25,6 +25,7 @@ export class WebsiteSwitcherSystray extends Component {
this.websiteService.goToWebsite({ websiteId: website.id });
}
},
class: website.id === this.websiteService.currentWebsite.id ? 'active' : '',
}));
}
}
@@ -3,12 +3,19 @@
<t t-name="website.WebsiteSwitcherSystray" owl="1">
<Dropdown class="'o_menu_systray_item o_website_switcher_container'" hotkey="'w'">
<t t-set-slot="toggler">
<span><t t-esc="websiteService.currentWebsite.name"/><i class="fa fa-caret-down ms-2"/></span>
<div class="d-none d-md-block">
<span t-esc="websiteService.currentWebsite.name" class="me-2"/>
<i class="fa fa-caret-down"/>
</div>
<div class="d-md-none">
<i class="fa fa-globe"/>
</div>
</t>
<t t-foreach="getElements()" t-as="element" t-key="element_index">
<DropdownItem
t-esc="element.name"
onSelected="element.callback"
class="element.class"
/>
</t>
</Dropdown>
@@ -25,11 +25,26 @@ function makeFakeWebsiteService() {
};
}
function makeFakeWebsiteCustomMenusService() {
return {
start() {
return {
get() {},
open() {},
addCustomMenus(sections) {
return sections;
},
};
}
};
}
const serviceRegistry = registry.category('services');
patch(utils, 'website_test_registries', {
prepareRegistriesWithCleanup() {
prepareRegistriesWithCleanup(...arguments);
serviceRegistry.add('website', makeFakeWebsiteService());
serviceRegistry.add('website_custom_menus', makeFakeWebsiteCustomMenusService());
clearRegistryWithCleanup(registry.category('website_systray'));
},
});
@@ -1,23 +1,14 @@
/** @odoo-module */
import { patch } from 'web.utils';
import { NavBar } from '@web/webclient/navbar/navbar';
import { registry } from '@web/core/registry';
import { EditMenuDialog } from '@website/components/dialog/edit_menu';
const { onWillStart } = owl;
patch(NavBar.prototype, 'website_events_navbar', {
setup() {
this._super();
onWillStart(() => {
this.websiteEditingMenus['website_event.menu_edit_menu'] = {
Component: EditMenuDialog,
isDisplayed: () => this.websiteService.currentWebsite && this.websiteService.currentWebsite.metadata.contentMenuId,
getProps: () => ({
rootID: parseInt(this.websiteService.currentWebsite.metadata.contentMenuId, 10),
}),
};
});
},
registry.category('website_custom_menus').add('website_event.menu_edit_menu', {
Component: EditMenuDialog,
isDisplayed: (env) => env.services.website.currentWebsite
&& env.services.website.currentWebsite.metadata.contentMenuId
&& !env.services.ui.isSmall,
getProps: (services) => ({
rootID: parseInt(services.website.currentWebsite.metadata.contentMenuId, 10),
}),
});
@@ -1,19 +1,8 @@
/** @odoo-module */
import { patch } from 'web.utils';
import { NavBar } from '@web/webclient/navbar/navbar';
import { registry } from '@web/core/registry';
const { onWillStart } = owl;
patch(NavBar.prototype, 'website_links_navbar', {
setup() {
this._super();
onWillStart(() => {
this.websiteEditingMenus['website_links.menu_link_tracker'] = {
openWidget: () => this.websiteService.goToWebsite({ path: `/r?u=${encodeURIComponent(this.websiteService.contentWindow.location.href)}` }),
isDisplayed: () => this.websiteService.currentWebsite && this.websiteService.contentWindow,
};
});
},
registry.category('website_custom_menus').add('website_links.menu_link_tracker', {
openWidget: (services) => services.website.goToWebsite({ path: `/r?u=${encodeURIComponent(services.website.contentWindow.location.href)}` }),
isDisplayed: (env) => env.services.website.currentWebsite && env.services.website.contentWindow,
});