diff --git a/addons/website/__manifest__.py b/addons/website/__manifest__.py
index a3fbdab52ae..e232a5d55e1 100644
--- a/addons/website/__manifest__.py
+++ b/addons/website/__manifest__.py
@@ -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',
diff --git a/addons/website/static/src/components/burger_menu/burger_menu.js b/addons/website/static/src/components/burger_menu/burger_menu.js
new file mode 100644
index 00000000000..f1ee3574f7d
--- /dev/null
+++ b/addons/website/static/src/components/burger_menu/burger_menu.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);
+ }
+ },
+});
diff --git a/addons/website/static/src/components/navbar/navbar.js b/addons/website/static/src/components/navbar/navbar.js
new file mode 100644
index 00000000000..f1db8fcaa4a
--- /dev/null
+++ b/addons/website/static/src/components/navbar/navbar.js
@@ -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);
+ },
+});
diff --git a/addons/website/static/src/components/switch/switch.js b/addons/website/static/src/components/switch/switch.js
index 45916125cc1..6f6586dbd98 100644
--- a/addons/website/static/src/components/switch/switch.js
+++ b/addons/website/static/src/components/switch/switch.js
@@ -22,6 +22,6 @@ Switch.template = xml`
`;
diff --git a/addons/website/static/src/components/switch/switch.scss b/addons/website/static/src/components/switch/switch.scss
index d61a73d95e9..24f13a5317a 100644
--- a/addons/website/static/src/components/switch/switch.scss
+++ b/addons/website/static/src/components/switch/switch.scss
@@ -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;
diff --git a/addons/website/static/src/services/website_custom_menus.js b/addons/website/static/src/services/website_custom_menus.js
index 48d7605b195..20a60496062 100644
--- a/addons/website/static/src/services/website_custom_menus.js
+++ b/addons/website/static/src/services/website_custom_menus.js
@@ -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']});
+ });
+ },
+ })
});
diff --git a/addons/website/static/src/services/website_service.js b/addons/website/static/src/services/website_service.js
index ebd86fa41c2..f2d72dea437 100644
--- a/addons/website/static/src/services/website_service.js
+++ b/addons/website/static/src/services/website_service.js
@@ -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,
diff --git a/addons/website/static/src/systray_items/edit_in_backend.xml b/addons/website/static/src/systray_items/edit_in_backend.xml
index 2d2f9614572..b4cbab5a06b 100644
--- a/addons/website/static/src/systray_items/edit_in_backend.xml
+++ b/addons/website/static/src/systray_items/edit_in_backend.xml
@@ -3,8 +3,8 @@
diff --git a/addons/website/static/src/systray_items/edit_website.xml b/addons/website/static/src/systray_items/edit_website.xml
index 66f9ecd899e..239f321fd3b 100644
--- a/addons/website/static/src/systray_items/edit_website.xml
+++ b/addons/website/static/src/systray_items/edit_website.xml
@@ -1,7 +1,7 @@
-