diff --git a/addons/fleet/static/src/js/fleet_form.js b/addons/fleet/static/src/js/fleet_form.js index 6cb35497a0b..50808f6b2e8 100644 --- a/addons/fleet/static/src/js/fleet_form.js +++ b/addons/fleet/static/src/js/fleet_form.js @@ -9,25 +9,18 @@ export class FleetFormController extends FormController { /** * @override **/ - getActionMenuItems() { - if (this.model.root.isInEdition) { - return {}; - } - - const menuItems = super.getActionMenuItems(); - const archiveAction = menuItems.other.find((item) => item.key === "archive"); - if (archiveAction) { - archiveAction.callback = () => { - const dialogProps = { - body: this.env._t( - "Every service and contract of this vehicle will be considered as archived. Are you sure that you want to archive this record?" - ), - confirm: () => this.model.root.archive(), - cancel: () => {}, - }; - this.dialogService.add(ConfirmationDialog, dialogProps); + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); + menuItems.archive.callback = () => { + const dialogProps = { + body: this.env._t( + "Every service and contract of this vehicle will be considered as archived. Are you sure that you want to archive this record?" + ), + confirm: () => this.model.root.archive(), + cancel: () => {}, }; - } + this.dialogService.add(ConfirmationDialog, dialogProps); + }; return menuItems; } } diff --git a/addons/hr/static/src/views/form_view.js b/addons/hr/static/src/views/form_view.js index b9aa209af4b..cdaed123937 100644 --- a/addons/hr/static/src/views/form_view.js +++ b/addons/hr/static/src/views/form_view.js @@ -15,16 +15,9 @@ export class EmployeeFormController extends FormController { this.archiveEmployee = useArchiveEmployee(); } - getActionMenuItems() { - const menuItems = super.getActionMenuItems(); - if (!this.archiveEnabled || !this.model.root.isActive) { - return menuItems; - } - - const archiveAction = menuItems.other.find((item) => item.key === "archive"); - if (archiveAction) { - archiveAction.callback = this.archiveEmployee.bind(this, this.model.root.resId); - } + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); + menuItems.archive.callback = this.archiveEmployee.bind(this, this.model.root.resId); return menuItems; } } diff --git a/addons/hr/static/src/views/list_view.js b/addons/hr/static/src/views/list_view.js index 6f1a322c5a6..803c44259a3 100644 --- a/addons/hr/static/src/views/list_view.js +++ b/addons/hr/static/src/views/list_view.js @@ -13,18 +13,13 @@ export class EmployeeListController extends ListController { this.archiveEmployee = useArchiveEmployee(); } - getActionMenuItems() { - const menuItems = super.getActionMenuItems(); + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); const selectedRecords = this.model.root.selection; // Only override the Archive action when only 1 record is selected. - if (!this.archiveEnabled || selectedRecords.length > 1 || !selectedRecords[0].data.active) { - return menuItems; - } - - const archiveAction = menuItems.other.find((item) => item.key === "archive"); - if (archiveAction) { - archiveAction.callback = this.archiveEmployee.bind(this, selectedRecords[0].resId); + if (selectedRecords.length === 1 && selectedRecords[0].data.active) { + menuItems.archive.callback = this.archiveEmployee.bind(this, selectedRecords[0].resId); } return menuItems; } diff --git a/addons/project/static/src/project_sharing/views/form/project_sharing_form_controller.js b/addons/project/static/src/project_sharing/views/form/project_sharing_form_controller.js index 652ab0b5b32..11ca80770d4 100644 --- a/addons/project/static/src/project_sharing/views/form/project_sharing_form_controller.js +++ b/addons/project/static/src/project_sharing/views/form/project_sharing_form_controller.js @@ -20,7 +20,7 @@ export class ProjectSharingFormController extends FormController { this.mailTemplate = mailTemplates.Mail; } - getActionMenuItems() { + get actionMenuItems() { return {}; } } diff --git a/addons/project/static/src/views/project_task_form/project_task_form_controller.js b/addons/project/static/src/views/project_task_form/project_task_form_controller.js index 9f929de9345..979f7c1a514 100644 --- a/addons/project/static/src/views/project_task_form/project_task_form_controller.js +++ b/addons/project/static/src/views/project_task_form/project_task_form_controller.js @@ -9,23 +9,15 @@ export class ProjectTaskFormController extends FormController { this.taskRecurrence = useService('project_task_recurrence'); } - getActionMenuItems() { - if (!(this.archiveEnabled && this.model.root.isActive) || !this.model.root.data.recurrence_id) { - return super.getActionMenuItems(); + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); + if (this.model.root.data.recurrence_id) { + menuItems.archive.callback = () => + this.taskRecurrence.stopRecurrence([this.model.root], () => + this.model.root.archive() + ); } - this.archiveEnabled = false; - const actionMenuItems = super.getActionMenuItems(); - this.archiveEnabled = true; - if (actionMenuItems) { - actionMenuItems.other.unshift({ - description: this.env._t('Archive'), - callback: () => this.taskRecurrence.stopRecurrence( - [this.model.root], - () => this.model.root.archive(), - ), - }); - } - return actionMenuItems; + return menuItems; } deleteRecord() { diff --git a/addons/project/static/src/views/project_task_list/project_task_list_controller.js b/addons/project/static/src/views/project_task_list/project_task_list_controller.js index 069a9dfc018..bc1d9480429 100644 --- a/addons/project/static/src/views/project_task_list/project_task_list_controller.js +++ b/addons/project/static/src/views/project_task_list/project_task_list_controller.js @@ -9,32 +9,17 @@ export class ProjectTaskListController extends ListController { this.taskRecurrence = useService('project_task_recurrence'); } - getActionMenuItems() { - if (!this.archiveEnabled || this.model.root.isM2MGrouped) { - return super.getActionMenuItems(); - } + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); const hasAnyRecurrences = this._anySelectedTasksWithRecurrence(); - this.archiveEnabled = !hasAnyRecurrences; - const actionMenuItems = super.getActionMenuItems(); - this.archiveEnabled = true; - if (actionMenuItems && hasAnyRecurrences) { - actionMenuItems.other.splice( - this.isExportEnable ? 1 : 0, - 0, - { - description: this.env._t('Archive'), - callback: () => this.taskRecurrence.stopRecurrence( - this.model.root.selection, - () => this.toggleArchiveState(true), - ), - }, - { - description: this.env._t('Unarchive'), - callback: () => this.toggleArchiveState(false), - }, - ); + + if (hasAnyRecurrences) { + menuItems.archive.callback = () => + this.taskRecurrence.stopRecurrence(this.model.root.selection, () => + this.toggleArchiveState(true) + ); } - return actionMenuItems; + return menuItems; } onDeleteSelectedRecords() { diff --git a/addons/stock/static/src/views/list/inventory_report_list_controller.js b/addons/stock/static/src/views/list/inventory_report_list_controller.js index a5065acab84..f4f1108f94a 100644 --- a/addons/stock/static/src/views/list/inventory_report_list_controller.js +++ b/addons/stock/static/src/views/list/inventory_report_list_controller.js @@ -35,17 +35,18 @@ export class InventoryReportListController extends ListController { }); } - getActionMenuItems() { - const actionMenus = super.getActionMenuItems(); - if (this.props.resModel === "stock.quant" && (!this.props.context.inventory_mode || this.props.context.inventory_report_mode)) { + get actionMenuItems() { + const actionMenus = super.actionMenuItems; + if ( + this.props.resModel === "stock.quant" && + (!this.props.context.inventory_mode || this.props.context.inventory_report_mode) + ) { // hack so we don't show some of the default actions when it's inappropriate to - const {print, action, other} = actionMenus; - return Object.assign( - {}, - print.filter(a => a.name !== 'Count Sheet'), - action.filter(a => a.name !== 'Set'), - { other: other }, - ); + const { print, action } = actionMenus; + return { + action: action.filter((a) => a.name !== "Set"), + print: print.filter((a) => a.name !== "Count Sheet"), + }; } return actionMenus; } diff --git a/addons/web/static/src/search/action_menus/action_menus.js b/addons/web/static/src/search/action_menus/action_menus.js index 7b5defe0e7f..00027d843df 100644 --- a/addons/web/static/src/search/action_menus/action_menus.js +++ b/addons/web/static/src/search/action_menus/action_menus.js @@ -3,13 +3,11 @@ import { browser } from "@web/core/browser/browser"; import { makeContext } from "@web/core/context"; import { session } from "@web/session"; -import { registry } from "@web/core/registry"; import { Dropdown } from "@web/core/dropdown/dropdown"; import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { useService } from "@web/core/utils/hooks"; import { Component, onWillStart, onWillUpdateProps } from "@odoo/owl"; -let registryActionId = 0; /** * Action menus (or Action/Print bar, previously called 'Sidebar') * @@ -25,10 +23,10 @@ export class ActionMenus extends Component { this.orm = useService("orm"); this.actionService = useService("action"); onWillStart(async () => { - this.actionItems = await this.setActionItems(this.props); + this.actionItems = await this.getActionItems(this.props); }); onWillUpdateProps(async (nextProps) => { - this.actionItems = await this.setActionItems(nextProps); + this.actionItems = await this.getActionItems(nextProps); }); } @@ -45,32 +43,18 @@ export class ActionMenus extends Component { // Private //--------------------------------------------------------------------- - async setActionItems(props) { - // Callback based actions - const callbackActions = (props.items.other || []).map((action) => - Object.assign({ key: `action-${action.description}` }, action) - ); - // Action based actions - const actionActions = props.items.action || []; - const formattedActions = actionActions.map((action) => ({ - action, - description: action.name, - key: action.id, - })); - // ActionMenus action registry components - const registryActions = []; - for (const { Component, getProps } of registry.category("action_menus").getAll()) { - const itemProps = await getProps(props, this.env); - if (itemProps) { - registryActions.push({ - Component, - key: `registry-action-${registryActionId++}`, - props: itemProps, - }); + async getActionItems(props) { + return (props.items.action || []).map((action) => { + if (action.callback) { + return Object.assign({ key: `action-${action.description}` }, action); + } else { + return { + action, + description: action.name, + key: action.id, + }; } - } - - return [...callbackActions, ...formattedActions, ...registryActions]; + }); } //--------------------------------------------------------------------- @@ -141,7 +125,6 @@ ActionMenus.props = { shape: { action: { type: Array, optional: true }, print: { type: Array, optional: true }, - other: { type: Array, optional: true }, }, }, onActionExecuted: { type: Function, optional: true }, diff --git a/addons/web/static/src/views/form/form_controller.js b/addons/web/static/src/views/form/form_controller.js index 2085696fc34..1d57981c92f 100644 --- a/addons/web/static/src/views/form/form_controller.js +++ b/addons/web/static/src/views/form/form_controller.js @@ -1,11 +1,13 @@ /** @odoo-module **/ +import { hasTouch } from "@web/core/browser/feature_detection"; import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; import { makeContext } from "@web/core/context"; import { useDebugCategory } from "@web/core/debug/debug_context"; import { registry } from "@web/core/registry"; import { SIZES } from "@web/core/ui/ui_service"; import { useBus, useService } from "@web/core/utils/hooks"; +import { omit } from "@web/core/utils/objects"; import { createElement } from "@web/core/utils/xml"; import { ActionMenus } from "@web/search/action_menus/action_menus"; import { Layout } from "@web/search/layout"; @@ -15,10 +17,9 @@ import { standardViewProps } from "@web/views/standard_view_props"; import { isX2Many } from "@web/views/utils"; import { useViewButtons } from "@web/views/view_button/view_button_hook"; import { useSetupView } from "@web/views/view_hook"; -import { hasTouch } from "@web/core/browser/feature_detection"; import { FormStatusIndicator } from "./form_status_indicator/form_status_indicator"; -import { Component, onWillStart, useEffect, useRef, onRendered, useState, toRaw } from "@odoo/owl"; +import { Component, onRendered, onWillStart, toRaw, useEffect, useRef, useState } from "@odoo/owl"; const viewRegistry = registry.category("views"); @@ -75,7 +76,11 @@ export async function loadSubViews( refinedContext.base_model_name = resModel; const comodel = field.relation; - const { fields: comodelFields, relatedModels, views } = await viewService.loadViews({ + const { + fields: comodelFields, + relatedModels, + views, + } = await viewService.loadViews({ resModel: comodel, views: [[false, viewType]], context: makeContext([fieldContext, userService.context, refinedContext]), @@ -304,48 +309,55 @@ export class FormController extends Component { this.router.pushState({ id: this.model.root.resId || undefined }); } - getActionMenuItems() { - const otherActionItems = []; - if (this.archiveEnabled) { - if (this.model.root.isActive) { - otherActionItems.push({ - key: "archive", - description: this.env._t("Archive"), - callback: () => { - const dialogProps = { - body: this.env._t( - "Are you sure that you want to archive all this record?" - ), - confirm: () => this.model.root.archive(), - cancel: () => {}, - }; - this.dialogService.add(ConfirmationDialog, dialogProps); - }, - }); - } else { - otherActionItems.push({ - key: "unarchive", - description: this.env._t("Unarchive"), - callback: () => this.model.root.unarchive(), - }); - } - } - if (this.archInfo.activeActions.create && this.archInfo.activeActions.duplicate) { - otherActionItems.push({ - key: "duplicate", + getStaticActionMenuItems() { + const { activeActions } = this.archInfo; + return { + archive: { + isAvailable: () => this.archiveEnabled && this.model.root.isActive, + sequence: 10, + description: this.env._t("Archive"), + callback: () => { + const dialogProps = { + body: this.env._t("Are you sure that you want to archive all this record?"), + confirm: () => this.model.root.archive(), + cancel: () => {}, + }; + this.dialogService.add(ConfirmationDialog, dialogProps); + }, + }, + unarchive: { + isAvailable: () => this.archiveEnabled && !this.model.root.isActive, + sequence: 20, + description: this.env._t("Unarchive"), + callback: () => this.model.root.unarchive(), + }, + duplicate: { + isAvailable: () => activeActions.create && activeActions.duplicate, + sequence: 30, description: this.env._t("Duplicate"), callback: () => this.duplicateRecord(), - }); - } - if (this.archInfo.activeActions.delete && !this.model.root.isVirtual) { - otherActionItems.push({ - key: "delete", + }, + delete: { + isAvailable: () => activeActions.delete && !this.model.root.isVirtual, + sequence: 40, description: this.env._t("Delete"), callback: () => this.deleteRecord(), skipSave: true, - }); - } - return Object.assign({}, this.props.info.actionMenus, { other: otherActionItems }); + }, + }; + } + + get actionMenuItems() { + const { actionMenus } = this.props.info; + const staticActionItems = Object.entries(this.getStaticActionMenuItems()) + .filter(([key, item]) => item.isAvailable === undefined || item.isAvailable()) + .sort(([k1, item1], [k2, item2]) => (item1.sequence || 0) - (item2.sequence || 0)) + .map(([key, item]) => Object.assign({ key }, omit(item, "isAvailable", "sequence"))); + + return { + action: [...staticActionItems, ...(actionMenus.action || [])], + print: actionMenus.print, + }; } async shouldExecuteAction(item) { diff --git a/addons/web/static/src/views/form/form_controller.xml b/addons/web/static/src/views/form/form_controller.xml index bc98334ac4a..a8f00f4047d 100644 --- a/addons/web/static/src/views/form/form_controller.xml +++ b/addons/web/static/src/views/form/form_controller.xml @@ -18,7 +18,7 @@ this.isExportEnable, + sequence: 10, description: this.env._t("Export"), callback: () => this.onExportData(), - }); - } - if (this.archiveEnabled && !isM2MGrouped) { - otherActionItems.push({ - key: "archive", + }, + archive: { + isAvailable: () => this.archiveEnabled && !isM2MGrouped, + sequence: 20, description: this.env._t("Archive"), callback: () => { const dialogProps = { @@ -279,21 +279,33 @@ export class ListController extends Component { }; this.dialogService.add(ConfirmationDialog, dialogProps); }, - }); - otherActionItems.push({ - key: "unarchive", + }, + unarchive: { + isAvailable: () => this.archiveEnabled && !isM2MGrouped, + sequence: 30, description: this.env._t("Unarchive"), callback: () => this.toggleArchiveState(false), - }); - } - if (this.activeActions.delete && !isM2MGrouped) { - otherActionItems.push({ - key: "delete", + }, + delete: { + isAvailable: () => this.activeActions.delete && !isM2MGrouped, + sequence: 40, description: this.env._t("Delete"), callback: () => this.onDeleteSelectedRecords(), - }); - } - return Object.assign({}, this.props.info.actionMenus, { other: otherActionItems }); + }, + }; + } + + get actionMenuItems() { + const { actionMenus } = this.props.info; + const staticActionItems = Object.entries(this.getStaticActionMenuItems()) + .filter(([key, item]) => item.isAvailable === undefined || item.isAvailable()) + .sort(([k1, item1], [k2, item2]) => (item1.sequence || 0) - (item2.sequence || 0)) + .map(([key, item]) => Object.assign({ key }, omit(item, "isAvailable", "sequence"))); + + return { + action: [...staticActionItems, ...(actionMenus.action || [])], + print: actionMenus.print, + }; } async onSelectDomain() { diff --git a/addons/web/static/src/views/list/list_controller.xml b/addons/web/static/src/views/list/list_controller.xml index 8b5d87108c5..a63c874bd99 100644 --- a/addons/web/static/src/views/list/list_controller.xml +++ b/addons/web/static/src/views/list/list_controller.xml @@ -20,7 +20,7 @@ getActiveIds="() => model.root.selection.map((r) => r.resId)" context="props.context" domain="props.domain" - items="getActionMenuItems()" + items="actionMenuItems" isDomainSelected="model.root.isDomainSelected" resModel="model.root.resModel" onActionExecuted="() => model.load()"/> diff --git a/addons/web/static/tests/helpers/mock_env.js b/addons/web/static/tests/helpers/mock_env.js index d30ace132f5..7dde26e59b6 100644 --- a/addons/web/static/tests/helpers/mock_env.js +++ b/addons/web/static/tests/helpers/mock_env.js @@ -73,7 +73,6 @@ function prepareRegistriesWithCleanup() { clearRegistryWithCleanup(registry.category("user_menuitems")); clearRegistryWithCleanup(registry.category("kanban_examples")); clearRegistryWithCleanup(registry.category("__processed_archs__")); - clearRegistryWithCleanup(registry.category("action_menus")); // fun fact: at least one registry is missing... this shows that we need a // better design for the way we clear these registries... } diff --git a/addons/web/static/tests/views/form/form_view_tests.js b/addons/web/static/tests/views/form/form_view_tests.js index 6f3dc902727..675e26c9c77 100644 --- a/addons/web/static/tests/views/form/form_view_tests.js +++ b/addons/web/static/tests/views/form/form_view_tests.js @@ -3845,6 +3845,109 @@ QUnit.module("Views", (hooks) => { ]); }); + QUnit.test("apply custom standard action menu (archive)", async function (assert) { + // add active field on partner model to have archive option + serverData.models.partner.fields.active = { string: "Active", type: "char", default: true }; + + const formView = registry.category("views").get("form"); + class CustomFormController extends formView.Controller { + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); + menuItems.archive.callback = () => { + assert.step("customArchive"); + }; + return menuItems; + } + } + registry.category("views").add("custom_form", { + ...formView, + Controller: CustomFormController, + }); + + await makeView({ + type: "form", + resModel: "partner", + serverData, + resId: 1, + actionMenus: {}, + arch: ` +
+ + + `, + }); + + assert.containsOnce(target, ".o_cp_action_menus"); + + await toggleActionMenu(target); + assert.containsOnce(target, ".o_cp_action_menus span:contains(Archive)"); + + await toggleMenuItem(target, "Archive"); + assert.verifySteps(["customArchive"]); + }); + + QUnit.test("add custom static action menu", async function (assert) { + const formView = registry.category("views").get("form"); + class CustomFormController extends formView.Controller { + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); + menuItems.customAvailable = { + isAvailable: () => true, + description: "Custom Available", + sequence: 35, + callback: () => { + assert.step("Custom Available"); + }, + }; + menuItems.customNotAvailable = { + isAvailable: () => false, + description: "Custom Not Available", + callback: () => { + assert.step("Custom Not Available"); + }, + }; + menuItems.customDefaultAvailable = { + description: "Custom Default Available", + callback: () => { + assert.step("Custom Default Available"); + }, + }; + return menuItems; + } + } + registry.category("views").add("custom_form", { + ...formView, + Controller: CustomFormController, + }); + + await makeView({ + type: "form", + resModel: "partner", + serverData, + resId: 1, + actionMenus: {}, + arch: ` +
+ + `, + }); + + assert.containsOnce(target, ".o_cp_action_menus"); + + await toggleActionMenu(target); + assert.deepEqual( + getNodesTextContent(target.querySelectorAll(".o_cp_action_menus .dropdown-item")), + ["Custom Default Available", "Duplicate", "Custom Available", "Delete"] + ); + + await toggleMenuItem(target, "Custom Available"); + assert.verifySteps(["Custom Available"]); + + await toggleActionMenu(target); + await toggleMenuItem(target, "Custom Default Available"); + assert.verifySteps(["Custom Default Available"]); + }); + QUnit.test("archive a record with intermediary action", async function (assert) { // add active field on partner model to have archive option serverData.models.partner.fields.active = { string: "Active", type: "char", default: true }; diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js index 144ebc51b5b..09892dac735 100644 --- a/addons/web/static/tests/views/list_view_tests.js +++ b/addons/web/static/tests/views/list_view_tests.js @@ -5334,6 +5334,108 @@ QUnit.module("Views", (hooks) => { ); }); + QUnit.test("apply custom static action menu (archive)", async function (assert) { + // add active field on foo model and make all records active + serverData.models.foo.fields.active = { string: "Active", type: "boolean", default: true }; + + const listView = registry.category("views").get("list"); + class CustomListController extends listView.Controller { + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); + menuItems.archive.callback = () => { + assert.step("customArchive"); + }; + return menuItems; + } + } + registry.category("views").add("custom_list", { + ...listView, + Controller: CustomListController, + }); + + await makeView({ + type: "list", + resModel: "foo", + serverData, + arch: ` + + + `, + actionMenus: {}, + }); + assert.containsNone(target, "div.o_control_panel .o_cp_action_menus"); + + await click(target, "thead .o_list_record_selector input"); + assert.containsOnce(target, "div.o_control_panel .o_cp_action_menus"); + + await toggleActionMenu(target); + await toggleMenuItem(target, "Archive"); + assert.verifySteps(["customArchive"]); + }); + + QUnit.test("add custom static action menu", async function (assert) { + const listView = registry.category("views").get("list"); + class CustomListController extends listView.Controller { + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); + menuItems.customAvailable = { + isAvailable: () => true, + description: "Custom Available", + sequence: 35, + callback: () => { + assert.step("Custom Available"); + }, + }; + menuItems.customNotAvailable = { + isAvailable: () => false, + description: "Custom Not Available", + callback: () => { + assert.step("Custom Not Available"); + }, + }; + menuItems.customDefaultAvailable = { + description: "Custom Default Available", + callback: () => { + assert.step("Custom Default Available"); + }, + }; + return menuItems; + } + } + registry.category("views").add("custom_list", { + ...listView, + Controller: CustomListController, + }); + + await makeView({ + type: "list", + resModel: "foo", + serverData, + arch: ` + + + `, + actionMenus: {}, + }); + assert.containsNone(target, "div.o_control_panel .o_cp_action_menus"); + + await click(target, "thead .o_list_record_selector input"); + assert.containsOnce(target, "div.o_control_panel .o_cp_action_menus"); + + await toggleActionMenu(target); + assert.deepEqual( + getNodesTextContent(target.querySelectorAll(".o_cp_action_menus .dropdown-item")), + ["Custom Default Available", "Export", "Custom Available", "Delete"] + ); + + await toggleMenuItem(target, "Custom Available"); + assert.verifySteps(["Custom Available"]); + + await toggleActionMenu(target); + await toggleMenuItem(target, "Custom Default Available"); + assert.verifySteps(["Custom Default Available"]); + }); + QUnit.test( "grouped, update the count of the group (and ancestors) when a record is deleted", async function (assert) { diff --git a/addons/website/static/src/components/views/page_list.js b/addons/website/static/src/components/views/page_list.js index 5a39a4f4503..b1724be600c 100644 --- a/addons/website/static/src/components/views/page_list.js +++ b/addons/website/static/src/components/views/page_list.js @@ -16,6 +16,9 @@ export class PageListController extends PageControllerMixin(listView.Controller) setup() { super.setup(); this.orm = useService('orm'); + if (this.props.resModel === "website.page") { + this.archiveEnabled = false; + } } /** @@ -30,28 +33,30 @@ export class PageListController extends PageControllerMixin(listView.Controller) * * @override */ - getActionMenuItems() { - const actionMenuItems = super.getActionMenuItems(); - // 'Archive' / 'Unarchive' options are disabled only on 'website.page' list view. - if (this.props.resModel === 'website.page') { - actionMenuItems.other = actionMenuItems.other - .filter(item => !['archive', 'unarchive'].includes(item.key)); - } - actionMenuItems.other.splice(-1, 0, { + getStaticActionMenuItems() { + const menuItems = super.getStaticActionMenuItems(); + menuItems.publish = { + sequence: 15, description: this.env._t("Publish"), callback: async () => { this.dialogService.add(ConfirmationDialog, { title: this.env._t("Publish Website Content"), - body: sprintf(this.env._t("%s record(s) selected, are you sure you want to publish them all?"), this.model.root.selection.length), + body: sprintf( + this.env._t( + "%s record(s) selected, are you sure you want to publish them all?" + ), + this.model.root.selection.length + ), confirm: () => this.togglePublished(true), }); - } - }, - { + }, + }; + menuItems.unpublish = { + sequence: 16, description: this.env._t("Unpublish"), callback: async () => this.togglePublished(false), - }); - return actionMenuItems; + }; + return menuItems; } onDeleteSelectedRecords() {