pgadmin4/web/pgadmin/browser/static/js/MainMenuFactory.js

264 lines
8.7 KiB
JavaScript

/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2025, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import gettext from 'sources/gettext';
import pgAdmin from 'sources/pgadmin';
import Menu, { MenuItem } from '../../../static/js/helpers/Menu';
import getApiInstance from '../../../static/js/api_instance';
import url_for from 'sources/url_for';
import withCheckPermission from './withCheckPermission';
const MAIN_MENUS = [
{ label: gettext('File'), name: 'file', id: 'mnu_file', index: 0, addSeprator: true, hasDynamicMenuItems: false },
{ label: gettext('Object'), name: 'object', id: 'mnu_obj', index: 1, addSeprator: true, hasDynamicMenuItems: true },
{ label: gettext('Tools'), name: 'tools', id: 'mnu_tools', index: 2, addSeprator: true, hasDynamicMenuItems: false },
{ label: gettext('Help'), name: 'help', id: 'mnu_help', index: 5, addSeprator: false, hasDynamicMenuItems: false }
];
export default class MainMenuFactory {
static electronCallbacks = {};
static toElectron() {
// we support 2 levels of submenu
return pgAdmin.Browser.MainMenus.map((m)=>{
return {
...m.serialize(),
submenu: m.menuItems.map((sm)=>{
const smName = `${m.name}_${sm.name}`;
MainMenuFactory.electronCallbacks[smName] = sm.callback;
return {
...sm.serialize(),
submenu: sm.getMenuItems()?.map((smsm)=>{
MainMenuFactory.electronCallbacks[`${smName}_${smsm.name}`] = smsm.callback;
return {
...smsm.serialize(),
};
})
};
})
};
});
}
static createMainMenus() {
pgAdmin.Browser.MainMenus = [];
MAIN_MENUS.forEach((_menu) => {
let menuObj = Menu.create(_menu.name, _menu.label, _menu.id, _menu.index, _menu.addSeprator, _menu.hasDynamicMenuItems);
pgAdmin.Browser.MainMenus.push(menuObj);
// Don't add menuItems for hasDynamicMenuItems true as it's menuItems get changed on tree selection.
if(!_menu.hasDynamicMenuItems) {
menuObj.clearMenuItems();
menuObj.addMenuItems(MainMenuFactory.createMenuItems(pgAdmin.Browser.all_menus_cache[_menu.name]));
}
});
// enable disable will take care of dynamic menus.
MainMenuFactory.enableDisableMenus();
window.electronUI?.onMenuClick((menuName)=>{
MainMenuFactory.electronCallbacks[menuName]?.();
});
window.electronUI?.setMenus(MainMenuFactory.toElectron());
}
static getSeparator(label, priority) {
return new MenuItem({type: 'separator', label, priority});
}
static createMenuItem(options) {
const callback = () => {
// Some callbacks registered in 'callbacks' check and call specifiec callback function
if (options.module && 'callbacks' in options.module && options.module.callbacks[options.callback]) {
options.module.callbacks[options.callback].apply(options.module, [options.data, pgAdmin.Browser.tree?.selected()]);
} else if (options?.module?.[options.callback]) {
options.module[options.callback](options.data, pgAdmin.Browser.tree?.selected());
} else if (options?.callback) {
options.callback(options);
} else if (options.url != '#') {
let api = getApiInstance();
api(
url_for('tools.initialize')
).then(()=>{
window.open(options.url);
}).catch(()=>{
pgAdmin.Browser.notifier.error(gettext('Error in opening window'));
});
}
};
return new MenuItem({...options, callback: withCheckPermission(options, callback)}, (menu, item)=> {
pgAdmin.Browser.Events.trigger('pgadmin:enable-disable-menu-items', menu, item);
window.electronUI?.enableDisableMenuItems(menu?.serialize(), item?.serialize());
});
}
static enableDisableMenus(item) {
let itemData = item ? pgAdmin.Browser.tree.itemData(item) : undefined;
const checkForItems = (items)=>{
items.forEach((mitem) => {
const subItems = mitem.getMenuItems() ?? [];
if(subItems.length > 0) {
checkForItems(subItems);
} else {
mitem.checkAndSetDisabled(itemData, item);
}
});
};
// Non dynamic menus will be required to check whether enabled/disabled.
pgAdmin.Browser.MainMenus.filter((m)=>(!m.hasDynamicMenuItems)).forEach((menu) => {
checkForItems(menu.getMenuItems());
});
pgAdmin.Browser.MainMenus.filter((m)=>(m.hasDynamicMenuItems)).forEach((menu) => {
let menuItemList = MainMenuFactory.getDynamicMenu(menu.name, item, itemData);
menu.setMenuItems(menuItemList);
});
// set the context menu as well
pgAdmin.Browser.BrowserContextMenu = MainMenuFactory.getDynamicMenu('context', item, itemData, true);
window.electronUI?.setMenus(MainMenuFactory.toElectron());
pgAdmin.Browser.Events.trigger('pgadmin:refresh-app-menu');
}
static checkNoMenuOptionForNode(itemData){
if(!itemData) {
return true;
}
let selectedNodeFromNodes=pgAdmin.Browser.Nodes[itemData._type];
let selectedNode=pgAdmin.Browser.tree.selected();
return selectedNodeFromNodes.showMenu?.(itemData, selectedNode) ?? true;
}
static createMenuItems(items, skipDisabled=false, checkAndSetDisabled=()=>true) {
let retVal = [];
let categories = {};
const getNewMenuItem = (i)=>{
const mi = MainMenuFactory.createMenuItem({...i});
checkAndSetDisabled?.(mi);
if(skipDisabled && mi.isDisabled) {
return null;
}
return mi;
};
const getMenuCategory = (catName)=>{
let category = pgAdmin.Browser.menu_categories[catName];
if(!category) {
// generate category on the fly.
category = {
name: catName,
label: catName,
priority: 10,
};
}
let cmi = categories[category.name];
if(!cmi) {
cmi = getNewMenuItem({...category});
// for easily finding again, note down.
categories[category.name] = cmi;
}
return cmi;
};
const applySeparators = (mi)=>{
const newItems = [];
if(mi.above) {
newItems.push(MainMenuFactory.getSeparator(mi.label, mi.priority));
}
newItems.push(mi);
if(mi.below) {
newItems.push(MainMenuFactory.getSeparator(mi.label, mi.priority));
}
return newItems;
};
Object.entries(items).forEach(([k, i])=>{
if('name' in i) {
const mi = getNewMenuItem(i);
if(!mi) return;
if((i.category??'common') != 'common') {
const cmi = getMenuCategory(i.category);
if(cmi) {
cmi.addMenuItems([...applySeparators(mi)]);
} else {
retVal.push(...applySeparators(mi));
}
} else {
retVal.push(...applySeparators(getNewMenuItem(i)));
}
} else {
// Can be a category
const cmi = getMenuCategory(k);
if(cmi) {
cmi.addMenuItems(MainMenuFactory.createMenuItems(i, skipDisabled, checkAndSetDisabled));
}
}
});
// Push the category menus
Object.values(categories).forEach((cmi)=>{
const items = cmi.getMenuItems();
// if there is only one menu in the category, then no need of the category.
if(items.length <= 1 && !cmi.single) {
retVal = retVal.concat(items);
return;
}
retVal.push(...applySeparators(cmi));
});
Menu.sortMenus(retVal ?? []);
return retVal;
}
static getDynamicMenu(name, item, itemData, skipDisabled=false) {
if(!item) {
return [MainMenuFactory.createMenuItem({
name: '',
label: gettext('No object selected'),
category: 'create',
priority: 1,
enable: false,
})];
}
const showMenu = MainMenuFactory.checkNoMenuOptionForNode(itemData);
if(!showMenu){
return [MainMenuFactory.createMenuItem({
enable : false,
label: gettext('No menu available for this object.'),
name:'',
priority: 1,
category: 'create',
})];
} else {
const nodeTypeMenus = pgAdmin.Browser.all_menus_cache[name]?.[itemData._type] ?? [];
const menuItemList = MainMenuFactory.createMenuItems(nodeTypeMenus, skipDisabled, (mi)=>{
return mi.checkAndSetDisabled(itemData, item);
});
if(menuItemList.length == 0) {
return [MainMenuFactory.createMenuItem({
enable : false,
label: gettext('No menu available for this object.'),
name:'',
priority: 1,
category: 'create',
})];
}
return menuItemList;
}
}
}