Rewrite pgAdmin main menu bar to use React. #5615
parent
d5e6786bc7
commit
ff9daec5ec
|
@ -389,7 +389,7 @@ function addCommonMenus(menu) {
|
||||||
|
|
||||||
let _menuItem = new gui.MenuItem({
|
let _menuItem = new gui.MenuItem({
|
||||||
label: menuItem.label,
|
label: menuItem.label,
|
||||||
enabled: !menuItem.is_disabled,
|
enabled: !menuItem.isDisabled,
|
||||||
type: menuItem.type || 'normal',
|
type: menuItem.type || 'normal',
|
||||||
priority: menuItem.priority,
|
priority: menuItem.priority,
|
||||||
...(submenu.items.length > 0) && {
|
...(submenu.items.length > 0) && {
|
||||||
|
@ -435,7 +435,7 @@ function getSubMenu(menuItem) {
|
||||||
let menuType = typeof item.checked == 'boolean' ? 'checkbox' : item.type;
|
let menuType = typeof item.checked == 'boolean' ? 'checkbox' : item.type;
|
||||||
submenu.append(new gui.MenuItem({
|
submenu.append(new gui.MenuItem({
|
||||||
label: item.label,
|
label: item.label,
|
||||||
enabled: !item.is_disabled,
|
enabled: !item.isDisabled,
|
||||||
priority: item.priority,
|
priority: item.priority,
|
||||||
type: menuType,
|
type: menuType,
|
||||||
checked: item.checked,
|
checked: item.checked,
|
||||||
|
@ -472,7 +472,7 @@ function addMacMenu(menu) {
|
||||||
new gui.MenuItem({
|
new gui.MenuItem({
|
||||||
label: menuItem.label,
|
label: menuItem.label,
|
||||||
type: menuItem.type || 'normal',
|
type: menuItem.type || 'normal',
|
||||||
enabled: !menuItem.is_disabled,
|
enabled: !menuItem.isDisabled,
|
||||||
priority: menuItem.priority,
|
priority: menuItem.priority,
|
||||||
...(submenu.items.length > 0) && {
|
...(submenu.items.length > 0) && {
|
||||||
submenu: submenu,
|
submenu: submenu,
|
||||||
|
@ -516,7 +516,7 @@ function enableDisableMenuItem(menu, menuItem) {
|
||||||
if (el?.label == menu?.label) {
|
if (el?.label == menu?.label) {
|
||||||
el.submenu.items.forEach((sub) => {
|
el.submenu.items.forEach((sub) => {
|
||||||
if (sub.label == menuItem.label) {
|
if (sub.label == menuItem.label) {
|
||||||
sub.enabled = !menuItem.is_disabled;
|
sub.enabled = !menuItem.isDisabled;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -560,7 +560,7 @@ function refreshMenuItems(menu) {
|
||||||
item.menu_items.forEach((subItem) => {
|
item.menu_items.forEach((subItem) => {
|
||||||
submenu.append(new gui.MenuItem({
|
submenu.append(new gui.MenuItem({
|
||||||
label: subItem.label,
|
label: subItem.label,
|
||||||
enabled: !subItem.is_disabled,
|
enabled: !subItem.isDisabled,
|
||||||
priority: subItem.priority,
|
priority: subItem.priority,
|
||||||
type: [true, false].includes(subItem.checked) ? 'checkbox' : 'normal',
|
type: [true, false].includes(subItem.checked) ? 'checkbox' : 'normal',
|
||||||
checked: subItem.checked,
|
checked: subItem.checked,
|
||||||
|
@ -572,8 +572,9 @@ function refreshMenuItems(menu) {
|
||||||
}
|
}
|
||||||
let _menuItem = new gui.MenuItem({
|
let _menuItem = new gui.MenuItem({
|
||||||
label: item.label,
|
label: item.label,
|
||||||
enabled: !item.is_disabled,
|
enabled: !item.isDisabled,
|
||||||
priority: item.priority,
|
priority: item.priority,
|
||||||
|
type: item.type,
|
||||||
...(submenu.items.length > 0) && {
|
...(submenu.items.length > 0) && {
|
||||||
submenu: submenu,
|
submenu: submenu,
|
||||||
},
|
},
|
||||||
|
@ -583,10 +584,6 @@ function refreshMenuItems(menu) {
|
||||||
});
|
});
|
||||||
|
|
||||||
el.submenu.append(_menuItem);
|
el.submenu.append(_menuItem);
|
||||||
if (['create', 'register'].includes(item.category)) {
|
|
||||||
let separator_menu = new gui.MenuItem({ type: 'separator' });
|
|
||||||
el.submenu.append(separator_menu);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -554,32 +554,18 @@ def index():
|
||||||
check_browser_upgrade()
|
check_browser_upgrade()
|
||||||
store_setting('LastUpdateCheck', today)
|
store_setting('LastUpdateCheck', today)
|
||||||
|
|
||||||
auth_only_internal = False
|
|
||||||
auth_source = []
|
|
||||||
|
|
||||||
session['allow_save_password'] = True
|
session['allow_save_password'] = True
|
||||||
|
|
||||||
if config.SERVER_MODE:
|
if config.SERVER_MODE and not config.MASTER_PASSWORD_REQUIRED and \
|
||||||
if session['auth_source_manager']['current_source'] == INTERNAL:
|
'pass_enc_key' in session:
|
||||||
auth_only_internal = True
|
session['allow_save_password'] = False
|
||||||
auth_source = session['auth_source_manager'][
|
|
||||||
'source_friendly_name']
|
|
||||||
|
|
||||||
if not config.MASTER_PASSWORD_REQUIRED and 'pass_enc_key' in session:
|
|
||||||
session['allow_save_password'] = False
|
|
||||||
|
|
||||||
response = Response(render_template(
|
response = Response(render_template(
|
||||||
MODULE_NAME + "/index.html",
|
MODULE_NAME + "/index.html",
|
||||||
username=current_user.username,
|
username=current_user.username,
|
||||||
auth_source=auth_source,
|
|
||||||
is_admin=current_user.has_role("Administrator"),
|
|
||||||
logout_url=get_logout_url(),
|
|
||||||
requirejs=True,
|
requirejs=True,
|
||||||
basejs=True,
|
basejs=True,
|
||||||
mfa_enabled=is_mfa_enabled(),
|
_=gettext
|
||||||
login_url=login_url,
|
|
||||||
_=gettext,
|
|
||||||
auth_only_internal=auth_only_internal,
|
|
||||||
))
|
))
|
||||||
|
|
||||||
# Set the language cookie after login, so next time the user will have that
|
# Set the language cookie after login, so next time the user will have that
|
||||||
|
@ -662,6 +648,16 @@ def utils():
|
||||||
|
|
||||||
for submodule in current_blueprint.submodules:
|
for submodule in current_blueprint.submodules:
|
||||||
snippets.extend(submodule.jssnippets)
|
snippets.extend(submodule.jssnippets)
|
||||||
|
|
||||||
|
auth_only_internal = False
|
||||||
|
auth_source = []
|
||||||
|
|
||||||
|
if config.SERVER_MODE:
|
||||||
|
if session['auth_source_manager']['current_source'] == INTERNAL:
|
||||||
|
auth_only_internal = True
|
||||||
|
auth_source = session['auth_source_manager'][
|
||||||
|
'source_friendly_name']
|
||||||
|
|
||||||
return make_response(
|
return make_response(
|
||||||
render_template(
|
render_template(
|
||||||
'browser/js/utils.js',
|
'browser/js/utils.js',
|
||||||
|
@ -684,6 +680,13 @@ def utils():
|
||||||
vw_edt_default_placeholder=VW_EDT_DEFAULT_PLACEHOLDER,
|
vw_edt_default_placeholder=VW_EDT_DEFAULT_PLACEHOLDER,
|
||||||
enable_psql=config.ENABLE_PSQL,
|
enable_psql=config.ENABLE_PSQL,
|
||||||
pgadmin_server_locale=default_locale,
|
pgadmin_server_locale=default_locale,
|
||||||
|
_=gettext,
|
||||||
|
auth_only_internal=auth_only_internal,
|
||||||
|
mfa_enabled=is_mfa_enabled(),
|
||||||
|
is_admin=current_user.has_role("Administrator"),
|
||||||
|
login_url=login_url,
|
||||||
|
username=current_user.username,
|
||||||
|
auth_source=auth_source
|
||||||
),
|
),
|
||||||
200, {'Content-Type': MIMETYPE_APP_JS})
|
200, {'Content-Type': MIMETYPE_APP_JS})
|
||||||
|
|
||||||
|
|
|
@ -8,55 +8,51 @@
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
import gettext from 'sources/gettext';
|
import gettext from 'sources/gettext';
|
||||||
import pgAdmin from 'sources/pgadmin';
|
import pgAdmin from 'sources/pgadmin';
|
||||||
import { getBrowser } from '../../../static/js/utils';
|
import Menu, { MenuItem } from '../../../static/js/helpers/Menu';
|
||||||
import Menu, { MenuItem } from './new_menu';
|
|
||||||
import getApiInstance from '../../../static/js/api_instance';
|
import getApiInstance from '../../../static/js/api_instance';
|
||||||
import url_for from 'sources/url_for';
|
import url_for from 'sources/url_for';
|
||||||
import Notifier from '../../../static/js/helpers/Notifier';
|
import Notifier from '../../../static/js/helpers/Notifier';
|
||||||
|
|
||||||
export let MainMenus = [
|
const MAIN_MENUS = [
|
||||||
{ label: gettext('File'), name: 'file', id: 'mnu_file', index: 0, addSepratior: true },
|
{ label: gettext('File'), name: 'file', id: 'mnu_file', index: 0, addSepratior: true },
|
||||||
{ label: gettext('Object'), name: 'object', id: 'mnu_obj', index: 1, addSepratior: true },
|
{ label: gettext('Object'), name: 'object', id: 'mnu_obj', index: 1, addSepratior: true },
|
||||||
{ label: gettext('Tools'), name: 'tools', id: 'mnu_tools', index: 2, addSepratior: true },
|
{ label: gettext('Tools'), name: 'tools', id: 'mnu_tools', index: 2, addSepratior: true },
|
||||||
{ label: gettext('Help'), name: 'help', id: 'mnu_help', index: 5, addSepratior: false }
|
{ label: gettext('Help'), name: 'help', id: 'mnu_help', index: 5, addSepratior: false }
|
||||||
];
|
];
|
||||||
|
|
||||||
let {name: browser} = getBrowser();
|
export default class MainMenuFactory {
|
||||||
|
static createMainMenus() {
|
||||||
export default function createMainMenus() {
|
pgAdmin.Browser.MainMenus = [];
|
||||||
pgAdmin.Browser.MainMenus = [];
|
MAIN_MENUS.forEach((_menu) => {
|
||||||
MainMenus.forEach((_menu) => {
|
let menuObj = Menu.create(_menu.name, _menu.label, _menu.id, _menu.index, _menu.addSepratior);
|
||||||
let menuObj = Menu.create(_menu.name, _menu.label, _menu.id, _menu.index, _menu.addSepratior);
|
pgAdmin.Browser.MainMenus.push(menuObj);
|
||||||
pgAdmin.Browser.MainMenus.push(menuObj);
|
// Don't add menuItems for Object menu as it's menuItems get changed on tree selection.
|
||||||
// Don't add menuItems for Object menu as it's menuItems get changed on tree selection.
|
if(_menu.name !== 'object') {
|
||||||
if(_menu.name !== 'object') {
|
menuObj.addMenuItems(Object.values(pgAdmin.Browser.all_menus_cache[_menu.name]));
|
||||||
menuObj.addMenuItems(Object.values(pgAdmin.Browser.menus[_menu.name]));
|
menuObj.getMenuItems().forEach((menuItem, index)=> {
|
||||||
menuObj.menuItems.forEach((menuItem, index)=> {
|
menuItem?.getMenuItems()?.forEach((item, indx)=> {
|
||||||
menuItem?.menu_items?.forEach((item, indx)=> {
|
item.below && menuItem?.getMenuItems().splice(indx+1, 0, MainMenuFactory.getSeparator());
|
||||||
item.below && menuItem?.menu_items.splice(indx+1, 0, getSeparator());
|
});
|
||||||
|
if(menuItem.below) {
|
||||||
|
menuObj.addMenuItem(MainMenuFactory.getSeparator(), index+1);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if(menuItem.below) {
|
}
|
||||||
menuObj.addMenuItem(getSeparator(), index+1);
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSeparator() {
|
pgAdmin.Browser.enable_disable_menus();
|
||||||
return new MenuItem({type: 'separator'});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function refreshMainMenuItems(menu, menuItems) {
|
static getSeparator(label, priority) {
|
||||||
if(browser == 'Nwjs') {
|
return new MenuItem({type: 'separator', label, priority});
|
||||||
|
}
|
||||||
|
|
||||||
|
static refreshMainMenuItems(menu, menuItems) {
|
||||||
menu.setMenuItems(menuItems);
|
menu.setMenuItems(menuItems);
|
||||||
pgAdmin.Browser.Events.trigger('pgadmin:nw-refresh-menu-item', menu);
|
pgAdmin.Browser.Events.trigger('pgadmin:nw-refresh-menu-item', menu);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Factory to create menu items for main menu.
|
static createMenuItem(options) {
|
||||||
export class MainMenuItemFactory {
|
|
||||||
static create(options) {
|
|
||||||
return new MenuItem({...options, callback: () => {
|
return new MenuItem({...options, callback: () => {
|
||||||
// Some callbacks registered in 'callbacks' check and call specifiec callback function
|
// Some callbacks registered in 'callbacks' check and call specifiec callback function
|
||||||
if (options.module && 'callbacks' in options.module && options.module.callbacks[options.callback]) {
|
if (options.module && 'callbacks' in options.module && options.module.callbacks[options.callback]) {
|
||||||
|
@ -83,4 +79,35 @@ export class MainMenuItemFactory {
|
||||||
pgAdmin.Browser.Events.trigger('pgadmin:nw-update-checked-menu-item', item);
|
pgAdmin.Browser.Events.trigger('pgadmin:nw-update-checked-menu-item', item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getContextMenu(menuList, item, node) {
|
||||||
|
Menu.sortMenus(menuList);
|
||||||
|
|
||||||
|
let ctxMenus = {};
|
||||||
|
let ctxIndex = 1;
|
||||||
|
menuList.forEach(ctx => {
|
||||||
|
let ctx_uid = _.uniqueId('ctx_');
|
||||||
|
let sub_ctx_item = {};
|
||||||
|
ctx.checkAndSetDisabled(node, item);
|
||||||
|
if (ctx.getMenuItems()) {
|
||||||
|
// Menu.sortMenus(ctx.getMenuItems());
|
||||||
|
ctx.getMenuItems().forEach((c) => {
|
||||||
|
c.checkAndSetDisabled(node, item);
|
||||||
|
if (!c.isDisabled) {
|
||||||
|
sub_ctx_item[ctx_uid + _.uniqueId('_sub_')] = c.getContextItem(c.label, c.isDisabled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!ctx.isDisabled) {
|
||||||
|
if(ctx.type == 'separator') {
|
||||||
|
ctxMenus[ctx_uid + '_' + ctx.priority + '_' + + ctxIndex + '_sep'] = '----';
|
||||||
|
} else {
|
||||||
|
ctxMenus[ctx_uid + '_' + ctx.priority + '_' + + ctxIndex + '_itm'] = ctx.getContextItem(ctx.label, ctx.isDisabled, sub_ctx_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctxIndex++;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ctxMenus;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -9,9 +9,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { generateNodeUrl } from './node_ajax';
|
import { generateNodeUrl } from './node_ajax';
|
||||||
import { getBrowser } from '../../../static/js/utils';
|
import MainMenuFactory from './MainMenuFactory';
|
||||||
import createMainMenus, {refreshMainMenuItems, MainMenuItemFactory} from './main_menu';
|
|
||||||
import { getContextMenu} from './new_menu';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import Notify, {initializeModalProvider, initializeNotifier} from '../../../static/js/helpers/Notifier';
|
import Notify, {initializeModalProvider, initializeNotifier} from '../../../static/js/helpers/Notifier';
|
||||||
import { checkMasterPassword } from '../../../static/js/Dialogs/index';
|
import { checkMasterPassword } from '../../../static/js/Dialogs/index';
|
||||||
|
@ -26,7 +24,7 @@ define('pgadmin.browser', [
|
||||||
'sources/tree/tree_init',
|
'sources/tree/tree_init',
|
||||||
'pgadmin.browser.utils', 'wcdocker', 'jquery.contextmenu',
|
'pgadmin.browser.utils', 'wcdocker', 'jquery.contextmenu',
|
||||||
'pgadmin.browser.preferences', 'pgadmin.browser.messages',
|
'pgadmin.browser.preferences', 'pgadmin.browser.messages',
|
||||||
'pgadmin.browser.menu', 'pgadmin.browser.panel', 'pgadmin.browser.layout',
|
'pgadmin.browser.panel', 'pgadmin.browser.layout',
|
||||||
'pgadmin.browser.runtime', 'pgadmin.browser.error', 'pgadmin.browser.frame',
|
'pgadmin.browser.runtime', 'pgadmin.browser.error', 'pgadmin.browser.frame',
|
||||||
'pgadmin.browser.node', 'pgadmin.browser.collection', 'pgadmin.browser.activity',
|
'pgadmin.browser.node', 'pgadmin.browser.collection', 'pgadmin.browser.activity',
|
||||||
'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
|
'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
|
||||||
|
@ -290,9 +288,9 @@ define('pgadmin.browser', [
|
||||||
// We also support showing dashboards, HTML file, external URL
|
// We also support showing dashboards, HTML file, external URL
|
||||||
frames: {},
|
frames: {},
|
||||||
/* Menus */
|
/* Menus */
|
||||||
// pgAdmin.Browser.MenuItem.add_menus(...) will register all the menus
|
// add_menus(...) will register all the menus
|
||||||
// in this container
|
// in this container
|
||||||
menus: {
|
all_menus_cache: {
|
||||||
// All context menu goes here under certain menu types.
|
// All context menu goes here under certain menu types.
|
||||||
// i.e. context: {'server': [...], 'server-group': [...]}
|
// i.e. context: {'server': [...], 'server-group': [...]}
|
||||||
context: {},
|
context: {},
|
||||||
|
@ -309,7 +307,8 @@ define('pgadmin.browser', [
|
||||||
// Help menus
|
// Help menus
|
||||||
help: {},
|
help: {},
|
||||||
},
|
},
|
||||||
native_context_menus: {},
|
MainMenus: [],
|
||||||
|
BrowserContextMenu: [],
|
||||||
|
|
||||||
add_panels: function() {
|
add_panels: function() {
|
||||||
/* Add hooked-in panels by extensions */
|
/* Add hooked-in panels by extensions */
|
||||||
|
@ -378,40 +377,37 @@ define('pgadmin.browser', [
|
||||||
let category = {
|
let category = {
|
||||||
'common': []
|
'common': []
|
||||||
};
|
};
|
||||||
for(let _key of Object.keys(obj.menus[name][d._type])){
|
const nodeTypeMenus = obj.all_menus_cache[name][d._type];
|
||||||
let menuCategory = obj.menus[name][d._type][_key].category;
|
for(let key of Object.keys(nodeTypeMenus)) {
|
||||||
let menuItem = obj.menus[name][d._type][_key];
|
let menuItem = nodeTypeMenus[key];
|
||||||
|
let menuCategory = menuItem.category ?? 'common';
|
||||||
if(menuCategory in this.menu_categories) {
|
category[menuCategory] = category[menuCategory] ?? [];
|
||||||
category[menuCategory] ? category[menuCategory].push(menuItem): category[menuCategory] = [menuItem];
|
category[menuCategory].push(menuItem);
|
||||||
} else if (!_.isUndefined(menuCategory)) {
|
|
||||||
category[menuCategory] ? category[menuCategory].push(menuItem): category[menuCategory] = [menuItem];
|
|
||||||
} else {
|
|
||||||
category['common'].push(menuItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let menuItemList = [];
|
let menuItemList = [];
|
||||||
|
|
||||||
for(let c in category) {
|
for(let c in category) {
|
||||||
|
|
||||||
if((c in obj.menu_categories || category[c].length > 1) && c != 'common' ) {
|
if((c in obj.menu_categories || category[c].length > 1) && c != 'common' ) {
|
||||||
let is_all_option_dis = true;
|
let allMenuItemsDisabled = true;
|
||||||
category[c].forEach((c)=> {
|
category[c].forEach((mi)=> {
|
||||||
c.is_disabled = c.disabled(d, item);
|
mi.checkAndSetDisabled(d, item);
|
||||||
|
if(allMenuItemsDisabled) {
|
||||||
c.setDisabled( c.is_disabled);
|
allMenuItemsDisabled = mi.isDisabled;
|
||||||
|
}
|
||||||
if(is_all_option_dis)
|
|
||||||
is_all_option_dis = c.is_disabled;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let label = c in obj.menu_categories ? obj.menu_categories[c].label : c;
|
const categoryMenuOptions = obj.menu_categories[c];
|
||||||
let priority = c in obj.menu_categories ? obj.menu_categories[c].priority || 10 : 10;
|
let label = categoryMenuOptions?.label ?? c;
|
||||||
if(!is_all_option_dis && skipDisabled){
|
let priority = categoryMenuOptions?.priority ?? 10;
|
||||||
let _menuItem = MainMenuItemFactory.create({
|
|
||||||
|
if(categoryMenuOptions?.above) {
|
||||||
|
menuItemList.push(MainMenuFactory.getSeparator(label, priority));
|
||||||
|
}
|
||||||
|
if(!allMenuItemsDisabled && skipDisabled) {
|
||||||
|
let _menuItem = MainMenuFactory.createMenuItem({
|
||||||
name: c,
|
name: c,
|
||||||
label: label,
|
label: label,
|
||||||
module:c,
|
module: c,
|
||||||
category: c,
|
category: c,
|
||||||
menu_items: category[c],
|
menu_items: category[c],
|
||||||
priority: priority
|
priority: priority
|
||||||
|
@ -419,7 +415,7 @@ define('pgadmin.browser', [
|
||||||
|
|
||||||
menuItemList.push(_menuItem);
|
menuItemList.push(_menuItem);
|
||||||
} else if(!skipDisabled){
|
} else if(!skipDisabled){
|
||||||
let _menuItem = MainMenuItemFactory.create({
|
let _menuItem = MainMenuFactory.createMenuItem({
|
||||||
name: c,
|
name: c,
|
||||||
label: label,
|
label: label,
|
||||||
module:c,
|
module:c,
|
||||||
|
@ -430,15 +426,18 @@ define('pgadmin.browser', [
|
||||||
|
|
||||||
menuItemList.push(_menuItem);
|
menuItemList.push(_menuItem);
|
||||||
}
|
}
|
||||||
|
if(categoryMenuOptions?.below) {
|
||||||
|
menuItemList.push(MainMenuFactory.getSeparator(label, priority));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
category[c].forEach((c)=> {
|
category[c].forEach((c)=> {
|
||||||
c.is_disabled = c.disabled(d, item);
|
c.checkAndSetDisabled(d, item);
|
||||||
});
|
});
|
||||||
|
|
||||||
category[c].forEach((m)=> {
|
category[c].forEach((m)=> {
|
||||||
if(!skipDisabled) {
|
if(!skipDisabled) {
|
||||||
menuItemList.push(m);
|
menuItemList.push(m);
|
||||||
} else if(skipDisabled && !m.is_disabled){
|
} else if(skipDisabled && !m.isDisabled){
|
||||||
menuItemList.push(m);
|
menuItemList.push(m);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -449,77 +448,37 @@ define('pgadmin.browser', [
|
||||||
},
|
},
|
||||||
// Enable/disable menu options
|
// Enable/disable menu options
|
||||||
enable_disable_menus: function(item) {
|
enable_disable_menus: function(item) {
|
||||||
// Mechanism to enable/disable menus depending on the condition.
|
let obj = this;
|
||||||
let obj = this,
|
let d = item ? obj.tree.itemData(item) : undefined;
|
||||||
// menu navigation bar
|
|
||||||
navbar = $('#navbar-menu > ul').first(),
|
|
||||||
// Drop down menu for objects
|
|
||||||
$obj_mnu = navbar.find('li#mnu_obj .dropdown-menu').first(),
|
|
||||||
// data for current selected object
|
|
||||||
d = item ? obj.tree.itemData(item) : undefined,
|
|
||||||
update_menuitem = function(m, o) {
|
|
||||||
if (m instanceof pgAdmin.Browser.MenuItem) {
|
|
||||||
m.update(d, item);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (let key in m) {
|
|
||||||
update_menuitem(m[key], o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
toolBar.enable(gettext('View Data'), false);
|
toolBar.enable(gettext('View Data'), false);
|
||||||
toolBar.enable(gettext('Filtered Rows'), false);
|
toolBar.enable(gettext('Filtered Rows'), false);
|
||||||
|
|
||||||
// All menus from the object menus (except the create drop-down
|
|
||||||
// menu) needs to be removed.
|
|
||||||
$obj_mnu.empty();
|
|
||||||
|
|
||||||
// All menus (except for the object menus) are already present.
|
// All menus (except for the object menus) are already present.
|
||||||
// They will just require to check, wheather they are
|
// They will just require to check, whether they are
|
||||||
// enabled/disabled.
|
// enabled/disabled.
|
||||||
let {name: browser} = getBrowser();
|
pgBrowser.MainMenus.filter((m)=>m.name != 'object').forEach((menu) => {
|
||||||
if(browser == 'Nwjs') {
|
menu.menuItems.forEach((mitem) => {
|
||||||
pgBrowser.MainMenus.forEach((menu) => {
|
mitem.checkAndSetDisabled(d, item);
|
||||||
menu.menuItems.forEach((mitem) => {
|
|
||||||
mitem.setDisabled(mitem.disabled(d, item));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}else {
|
});
|
||||||
_.each([
|
|
||||||
{name: 'file', id: '#mnu_file', label: gettext('File')},
|
|
||||||
{name: 'management', id: '#mnu_management', label: gettext('Management')},
|
|
||||||
{name: 'tools', id: '#mnu_tools', label: gettext('Tools')},
|
|
||||||
{name: 'help', id:'#mnu_help', label: gettext('Help')}], function(o) {
|
|
||||||
_.each( obj.menus[o.name], function(m) {
|
|
||||||
update_menuitem(m, o);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the object menu dynamically
|
// Create the object menu dynamically
|
||||||
if (item && obj.menus['object'] && obj.menus['object'][d._type]) {
|
let objectMenu = pgBrowser.MainMenus.find((menu) => menu.name == 'object');
|
||||||
if(browser == 'Nwjs') {
|
if (item && obj.all_menus_cache['object']?.[d._type]) {
|
||||||
let menuItemList = obj.getMenuList('object', item, d);
|
let menuItemList = obj.getMenuList('object', item, d);
|
||||||
let objectMenu = pgBrowser.MainMenus.filter((menu) => menu.name == 'object');
|
objectMenu && MainMenuFactory.refreshMainMenuItems(objectMenu, menuItemList);
|
||||||
objectMenu.length > 0 && refreshMainMenuItems(objectMenu[0], menuItemList);
|
let ctxMenuList = obj.getMenuList('context', item, d, true);
|
||||||
let ctxMenuList = obj.getMenuList('context', item, d, true);
|
obj.BrowserContextMenu = MainMenuFactory.getContextMenu(ctxMenuList, item, d);
|
||||||
obj.native_context_menus = getContextMenu(ctxMenuList, item, d);
|
|
||||||
} else {
|
|
||||||
pgAdmin.Browser.MenuCreator(
|
|
||||||
obj.Nodes, $obj_mnu, obj.menus['object'][d._type], obj.menu_categories, d, item
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Create a dummy 'no object seleted' menu
|
objectMenu && MainMenuFactory.refreshMainMenuItems(objectMenu, [
|
||||||
let create_submenu = pgAdmin.Browser.MenuGroup(
|
MainMenuFactory.createMenuItem({
|
||||||
obj.menu_categories['create'], [{
|
name: '',
|
||||||
$el: $('<li><a class="dropdown-item disabled" href="#" role="menuitem">' + gettext('No object selected') + '</a></li>'),
|
label: gettext('No object selected'),
|
||||||
priority: 1,
|
|
||||||
category: 'create',
|
category: 'create',
|
||||||
update: function() {/*This is intentional (SonarQube)*/},
|
priority: 1,
|
||||||
}], false);
|
enable: false,
|
||||||
$obj_mnu.append(create_submenu.$el);
|
})
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
init: function() {
|
init: function() {
|
||||||
|
@ -611,20 +570,10 @@ define('pgadmin.browser', [
|
||||||
autoHide: false,
|
autoHide: false,
|
||||||
build: function(element) {
|
build: function(element) {
|
||||||
let item = obj.tree.itemFrom(element),
|
let item = obj.tree.itemFrom(element),
|
||||||
d = obj.tree.itemData(item),
|
|
||||||
menus = obj.menus['context'][d._type],
|
|
||||||
$div = $('<div></div>'),
|
|
||||||
context_menu = {};
|
context_menu = {};
|
||||||
|
|
||||||
if(item) obj.tree.select(item);
|
if(item) obj.tree.select(item);
|
||||||
let {name: browser} = getBrowser();
|
context_menu = obj.BrowserContextMenu;
|
||||||
if(browser == 'Nwjs'){
|
|
||||||
context_menu = obj.native_context_menus;
|
|
||||||
} else {
|
|
||||||
pgAdmin.Browser.MenuCreator(
|
|
||||||
obj.Nodes, $div, menus, obj.menu_categories, d, item, context_menu
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
autoHide: false,
|
autoHide: false,
|
||||||
|
@ -641,7 +590,6 @@ define('pgadmin.browser', [
|
||||||
|
|
||||||
// Register scripts and add menus
|
// Register scripts and add menus
|
||||||
pgBrowser.utils.registerScripts(this);
|
pgBrowser.utils.registerScripts(this);
|
||||||
pgBrowser.utils.addMenus(obj);
|
|
||||||
|
|
||||||
let headers = {};
|
let headers = {};
|
||||||
headers[pgAdmin.csrf_token_header] = pgAdmin.csrf_token;
|
headers[pgAdmin.csrf_token_header] = pgAdmin.csrf_token;
|
||||||
|
@ -780,16 +728,11 @@ define('pgadmin.browser', [
|
||||||
// Add menus of module/extension at appropriate menu
|
// Add menus of module/extension at appropriate menu
|
||||||
add_menus: function(menus) {
|
add_menus: function(menus) {
|
||||||
let self = this,
|
let self = this,
|
||||||
pgMenu = this.menus,
|
pgMenu = this.all_menus_cache;
|
||||||
MenuItem = pgAdmin.Browser.MenuItem;
|
|
||||||
let {name: browser} = getBrowser();
|
|
||||||
// MenuItem = pgAdmin.Browser.MenuItem;
|
|
||||||
_.each(menus, function(m) {
|
_.each(menus, function(m) {
|
||||||
_.each(m.applies, function(a) {
|
_.each(m.applies, function(a) {
|
||||||
/* We do support menu type only from this list */
|
/* We do support menu type only from this list */
|
||||||
// if ($.inArray(a, [
|
|
||||||
// 'context', 'file', 'edit', 'object',
|
|
||||||
// 'management', 'tools', 'help']) >= 0) {
|
|
||||||
if(['context', 'file', 'edit', 'object','management', 'tools', 'help'].indexOf(a) > -1){
|
if(['context', 'file', 'edit', 'object','management', 'tools', 'help'].indexOf(a) > -1){
|
||||||
let _menus;
|
let _menus;
|
||||||
|
|
||||||
|
@ -835,42 +778,23 @@ define('pgadmin.browser', [
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if(browser == 'Nwjs') {
|
return MainMenuFactory.createMenuItem({
|
||||||
return MainMenuItemFactory.create({
|
name: _m.name,
|
||||||
name: _m.name,
|
label: _m.label,
|
||||||
label: _m.label,
|
module: _m.module,
|
||||||
module: _m.module,
|
category: _m.category,
|
||||||
category: _m.category,
|
callback: typeof _m.module == 'object' && _m.module[_m.callback] && _m.callback in _m.module[_m.callback] ? _m.module[_m.callback] : _m.callback,
|
||||||
callback: typeof _m.module == 'object' && _m.module[_m.callback] && _m.callback in _m.module[_m.callback] ? _m.module[_m.callback] : _m.callback,
|
priority: _m.priority,
|
||||||
priority: _m.priority,
|
data: _m.data,
|
||||||
data: _m.data,
|
url: _m.url || '#',
|
||||||
url: _m.url || '#',
|
target: _m.target,
|
||||||
target: _m.target,
|
icon: _m.icon,
|
||||||
icon: _m.icon,
|
enable: enable ? enable : true,
|
||||||
enable: enable ? enable : true,
|
node: _m.node,
|
||||||
node: _m.node,
|
checked: _m.checked,
|
||||||
checked: _m.checked,
|
below: _m.below,
|
||||||
below: _m.below,
|
applies: _m.applies,
|
||||||
applies: _m.applies,
|
});
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return new MenuItem({
|
|
||||||
name: _m.name,
|
|
||||||
label: _m.label,
|
|
||||||
module: _m.module,
|
|
||||||
category: _m.category,
|
|
||||||
callback: _m.callback,
|
|
||||||
priority: _m.priority,
|
|
||||||
data: _m.data,
|
|
||||||
url: _m.url || '#',
|
|
||||||
target: _m.target,
|
|
||||||
icon: _m.icon,
|
|
||||||
enable,
|
|
||||||
node: _m.node,
|
|
||||||
checked: _m.checked,
|
|
||||||
below: _m.below,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!_.has(_menus, m.name)) {
|
if (!_.has(_menus, m.name)) {
|
||||||
|
@ -895,62 +819,6 @@ define('pgadmin.browser', [
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Create the menus
|
|
||||||
create_menus: function () {
|
|
||||||
let { name: browser } = getBrowser();
|
|
||||||
// Add Native menus if NWjs app
|
|
||||||
if (browser == 'Nwjs') {
|
|
||||||
createMainMenus();
|
|
||||||
this.enable_disable_menus();
|
|
||||||
} else {
|
|
||||||
/* Create menus */
|
|
||||||
let navbar = $('#navbar-menu > ul').first();
|
|
||||||
let obj = this;
|
|
||||||
|
|
||||||
_.each([
|
|
||||||
{ menu: 'file', id: '#mnu_file' },
|
|
||||||
{ menu: 'management', id: '#mnu_management' },
|
|
||||||
{ menu: 'tools', id: '#mnu_tools' },
|
|
||||||
{ menu: 'help', id: '#mnu_help' }],
|
|
||||||
function (o) {
|
|
||||||
let $mnu = navbar.children(o.id).first(),
|
|
||||||
$dropdown = $mnu.children('.dropdown-menu').first();
|
|
||||||
$dropdown.empty();
|
|
||||||
if (o.menu == 'help') {
|
|
||||||
$dropdown.append('<div id="quick-search-component"></div>');
|
|
||||||
$dropdown.append('<div class="menu-groups"><span class="fa fa-list" style="font-weight:900 !important;"></span> ' + gettext('SUGGESTED SITES') + '</div>');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pgAdmin.Browser.MenuCreator(
|
|
||||||
obj.Nodes, $dropdown, obj.menus[o.menu], obj.menu_categories
|
|
||||||
)) {
|
|
||||||
$mnu.removeClass('d-none');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
navbar.children('#mnu_obj').removeClass('d-none');
|
|
||||||
obj.enable_disable_menus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// General function to handle callbacks for object or dialog help.
|
|
||||||
showHelp: function(type, url, node, item) {
|
|
||||||
if (type == 'object_help') {
|
|
||||||
// Construct the URL
|
|
||||||
let server = pgBrowser.tree.getTreeNodeHierarchy(item).server;
|
|
||||||
let baseUrl = pgBrowser.utils.pg_help_path;
|
|
||||||
let fullUrl = help.getHelpUrl(baseUrl, url, server.version);
|
|
||||||
|
|
||||||
window.open(fullUrl, 'postgres_help');
|
|
||||||
} else if(type == 'dialog_help') {
|
|
||||||
if (pgWindow && pgWindow.default) {
|
|
||||||
pgWindow.default.open(url, 'pgadmin_help');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
window.open(url, 'pgadmin_help');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$('#live-search-field').focus();
|
|
||||||
},
|
|
||||||
_findTreeChildNode: function(_i, _d, _o) {
|
_findTreeChildNode: function(_i, _d, _o) {
|
||||||
let loaded = _o.t.wasLoad(_i),
|
let loaded = _o.t.wasLoad(_i),
|
||||||
onLoad = function() {
|
onLoad = function() {
|
||||||
|
@ -2224,27 +2092,6 @@ define('pgadmin.browser', [
|
||||||
brace_matching: pgBrowser.utils.braceMatching,
|
brace_matching: pgBrowser.utils.braceMatching,
|
||||||
indent_with_tabs: pgBrowser.utils.is_indent_with_tabs,
|
indent_with_tabs: pgBrowser.utils.is_indent_with_tabs,
|
||||||
},
|
},
|
||||||
|
|
||||||
// This function will return the name and version of the browser.
|
|
||||||
get_browser: function() {
|
|
||||||
let ua=navigator.userAgent,tem,M=ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
|
|
||||||
if(/trident/i.test(M[1])) {
|
|
||||||
tem=/\brv[ :]+(\d+)/g.exec(ua) || [];
|
|
||||||
return {name:'IE', version:(tem[1]||'')};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(M[1]==='Chrome') {
|
|
||||||
tem=ua.match(/\bOPR|Edge\/(\d+)/);
|
|
||||||
if(tem!=null) {return {name:tem[0], version:tem[1]};}
|
|
||||||
}
|
|
||||||
|
|
||||||
M=M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
|
|
||||||
if((tem=ua.match(/version\/(\d+)/i))!=null) {M.splice(1,1,tem[1]);}
|
|
||||||
return {
|
|
||||||
name: M[0],
|
|
||||||
version: M[1],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Remove paste event mapping from CodeMirror's emacsy KeyMap binding
|
/* Remove paste event mapping from CodeMirror's emacsy KeyMap binding
|
||||||
|
@ -2258,9 +2105,6 @@ define('pgadmin.browser', [
|
||||||
if (pgBrowser.utils.useSpaces == 'True') {
|
if (pgBrowser.utils.useSpaces == 'True') {
|
||||||
pgAdmin.Browser.editor_shortcut_keys.Tab = 'insertSoftTab';
|
pgAdmin.Browser.editor_shortcut_keys.Tab = 'insertSoftTab';
|
||||||
}
|
}
|
||||||
setTimeout(function(){
|
|
||||||
$('#mnu_about').closest('li').before('<li class="dropdown-divider"></li>');
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
return pgAdmin.Browser;
|
return pgAdmin.Browser;
|
||||||
});
|
});
|
||||||
|
|
|
@ -113,8 +113,8 @@ _.extend(pgBrowser, {
|
||||||
|
|
||||||
lock_layout: function(docker, op) {
|
lock_layout: function(docker, op) {
|
||||||
let menu_items = [];
|
let menu_items = [];
|
||||||
if('mnu_locklayout' in this.menus['file']) {
|
if('mnu_locklayout' in this.all_menus_cache['file']) {
|
||||||
menu_items = this.menus['file']['mnu_locklayout']['menu_items'];
|
menu_items = this.all_menus_cache['file']['mnu_locklayout']['menu_items'];
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(op) {
|
switch(op) {
|
||||||
|
|
|
@ -1,521 +0,0 @@
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// pgAdmin 4 - PostgreSQL Tools
|
|
||||||
//
|
|
||||||
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
|
|
||||||
// This software is released under the PostgreSQL Licence
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////
|
|
||||||
import _ from 'lodash';
|
|
||||||
import {MenuItem as NewMenuItem} from './new_menu';
|
|
||||||
|
|
||||||
define([
|
|
||||||
'sources/pgadmin', 'jquery', 'sources/utils', 'sources/gettext',
|
|
||||||
], function(pgAdmin, $, pgadminUtils, gettext) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
pgAdmin.Browser = pgAdmin.Browser || {};
|
|
||||||
|
|
||||||
// Individual menu-item class
|
|
||||||
let MenuItem = pgAdmin.Browser.MenuItem = function(opts) {
|
|
||||||
let menu_opts = [
|
|
||||||
'name', 'label', 'priority', 'module', 'callback', 'data', 'enable',
|
|
||||||
'category', 'target', 'url' /* Do not show icon in the menus, 'icon' */ , 'node',
|
|
||||||
'checked', 'below', 'menu_items',
|
|
||||||
],
|
|
||||||
defaults = {
|
|
||||||
url: '#',
|
|
||||||
target: '_self',
|
|
||||||
enable: true,
|
|
||||||
};
|
|
||||||
_.extend(this, defaults, _.pick(opts, menu_opts));
|
|
||||||
};
|
|
||||||
|
|
||||||
_.extend(pgAdmin.Browser.MenuItem.prototype, {
|
|
||||||
/*
|
|
||||||
* Keeps track of the jQuery object representing this menu-item. This will
|
|
||||||
* be used by the update function to enable/disable the individual item.
|
|
||||||
*/
|
|
||||||
$el: null,
|
|
||||||
/*
|
|
||||||
* Generate the UI for this menu-item. enable/disable, based on the
|
|
||||||
* currently selected object.
|
|
||||||
*/
|
|
||||||
generate: function(node, item) {
|
|
||||||
this.create_el(node, item);
|
|
||||||
|
|
||||||
this.context = {
|
|
||||||
name: this.label,
|
|
||||||
/* icon: this.icon || this.module && (this.module.type), */
|
|
||||||
disabled: this.is_disabled,
|
|
||||||
callback: this.context_menu_callback.bind(this, item),
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.$el;
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create the jquery element for the menu-item.
|
|
||||||
*/
|
|
||||||
create_el: function(node, item) {
|
|
||||||
|
|
||||||
if(this.menu_items) {
|
|
||||||
_.each(this.menu_items, function(submenu_item){
|
|
||||||
submenu_item.generate(node, item);
|
|
||||||
});
|
|
||||||
let create_submenu = pgAdmin.Browser.MenuGroup({
|
|
||||||
'label': this.label,
|
|
||||||
'id': this.name,
|
|
||||||
}, this.menu_items);
|
|
||||||
this.$el = create_submenu.$el;
|
|
||||||
} else {
|
|
||||||
let data_disabled = null;
|
|
||||||
if(this.data != undefined && this.data.data_disabled != undefined){
|
|
||||||
data_disabled = this.data.data_disabled;
|
|
||||||
}
|
|
||||||
let url = $('<a></a>', {
|
|
||||||
'id': this.name,
|
|
||||||
'href': this.url,
|
|
||||||
'target': this.target,
|
|
||||||
'data-toggle': 'pg-menu',
|
|
||||||
'role': 'menuitem',
|
|
||||||
'data-disabled': data_disabled,
|
|
||||||
}).data('pgMenu', {
|
|
||||||
module: this.module || pgAdmin.Browser,
|
|
||||||
cb: this.callback,
|
|
||||||
data: this.data,
|
|
||||||
}).addClass('dropdown-item');
|
|
||||||
|
|
||||||
this.is_disabled = this.disabled(node, item);
|
|
||||||
if (this.icon) {
|
|
||||||
url.append($('<i></i>', {
|
|
||||||
'class': this.icon,
|
|
||||||
}));
|
|
||||||
} else if(!_.isUndefined(this.checked)) {
|
|
||||||
url.append($('<i></i>', {
|
|
||||||
'class': 'fa fa-check '+ (this.checked?'':'visibility-hidden'),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
url.addClass((this.is_disabled ? ' disabled' : ''));
|
|
||||||
|
|
||||||
let textSpan = $('<span data-test="menu-item-text"></span>').text(' ' + this.label);
|
|
||||||
|
|
||||||
url.append(textSpan);
|
|
||||||
|
|
||||||
let mnu_element = $('<li/>').append(url);
|
|
||||||
// Check if below parameter is defined and true then we need to add
|
|
||||||
// separator.
|
|
||||||
if (!_.isUndefined(this.below) && this.below === true) {
|
|
||||||
mnu_element.append('<li class="dropdown-divider"></li>');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$el = mnu_element;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
* Updates the enable/disable state of the menu-item based on the current
|
|
||||||
* selection using the disabled function. This also creates a object
|
|
||||||
* for this menu, which can be used in the context-menu.
|
|
||||||
*/
|
|
||||||
update: function(node, item) {
|
|
||||||
|
|
||||||
if (this.$el && !this.$el.find('.dropdown-item').hasClass('disabled')) {
|
|
||||||
this.$el.find('.dropdown-item').addClass('disabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.is_disabled = this.disabled(node, item);
|
|
||||||
if (this.$el && !this.is_disabled) {
|
|
||||||
this.$el.find('.dropdown-item').removeClass('disabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context = {
|
|
||||||
name: this.label,
|
|
||||||
/* icon: this.icon || (this.module && this.module.type), */
|
|
||||||
disabled: this.is_disabled,
|
|
||||||
callback: this.context_menu_callback.bind(this, item),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This will be called when context-menu is clicked.
|
|
||||||
*/
|
|
||||||
context_menu_callback: function(item) {
|
|
||||||
let o = this,
|
|
||||||
cb;
|
|
||||||
|
|
||||||
if (o.module['callbacks'] && (
|
|
||||||
o.callback in o.module['callbacks']
|
|
||||||
)) {
|
|
||||||
cb = o.module['callbacks'][o.callback];
|
|
||||||
} else if (o.callback in o.module) {
|
|
||||||
cb = o.module[o.callback];
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb.apply(o.module, [o.data, item]);
|
|
||||||
} else {
|
|
||||||
pgAdmin.Browser.report_error(
|
|
||||||
pgadminUtils.sprintf('Developer Warning: Callback - "%s" not found!', o.cb)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Checks this menu enable/disable state based on the selection.
|
|
||||||
*/
|
|
||||||
disabled: function(node, item) {
|
|
||||||
if (this.enable == undefined) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.node) {
|
|
||||||
if (!node) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (_.isArray(this.node) ? (
|
|
||||||
_.indexOf(this.node, node) == -1
|
|
||||||
) : (this.node != node._type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_.isBoolean(this.enable)) return !this.enable;
|
|
||||||
if (_.isFunction(this.enable)) return !this.enable.apply(this.module, [node, item, this.data]);
|
|
||||||
if (this.module && _.isBoolean(this.module[this.enable])) return !this.module[this.enable];
|
|
||||||
if (this.module && _.isFunction(this.module[this.enable])) return !(this.module[this.enable]).apply(this.module, [node, item, this.data]);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Change the checked value and update the checked icon on the menu
|
|
||||||
*/
|
|
||||||
change_checked(isChecked) {
|
|
||||||
if(!_.isUndefined(this.checked)) {
|
|
||||||
this.checked = isChecked;
|
|
||||||
if(this.checked) {
|
|
||||||
this.$el.find('.fa-check').removeClass('visibility-hidden');
|
|
||||||
} else {
|
|
||||||
this.$el.find('.fa-check').addClass('visibility-hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This a class for creating a menu group, mainly used by the submenu
|
|
||||||
* creation logic.
|
|
||||||
*
|
|
||||||
* Arguments:
|
|
||||||
* 1. Options to render the submenu DOM Element.
|
|
||||||
* i.e. label, icon, above (separator), below (separator)
|
|
||||||
* 2. List of menu-items comes under this submenu.
|
|
||||||
* 3. Did we rendered separator after the menu-item/submenu?
|
|
||||||
* 4. A unique-id for this menu-item.
|
|
||||||
*
|
|
||||||
* Returns a object, similar to the menu-item, which has his own jQuery
|
|
||||||
* Element, context menu representing object, etc.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
pgAdmin.Browser.MenuGroup = function(opts, items, prev, ctx) {
|
|
||||||
let template = _.template([
|
|
||||||
'<% if (above) { %><li class="dropdown-divider"></li><% } %>',
|
|
||||||
'<li class="dropdown-submenu" role="menuitem">',
|
|
||||||
' <a href="#" class="dropdown-item">',
|
|
||||||
' <% if (icon) { %><i class="<%= icon %>"></i><% } %>',
|
|
||||||
' <span><%= label %></span>',
|
|
||||||
' </a>',
|
|
||||||
' <ul class="dropdown-menu">',
|
|
||||||
' </ul>',
|
|
||||||
'</li>',
|
|
||||||
'<% if (below) { %><li class="dropdown-divider"></li><% } %>',
|
|
||||||
].join('\n')),
|
|
||||||
data = {
|
|
||||||
'label': opts.label,
|
|
||||||
'icon': opts.icon,
|
|
||||||
'above': opts.above && !prev,
|
|
||||||
'below': opts.below,
|
|
||||||
},
|
|
||||||
m,
|
|
||||||
$el = $(template(data)),
|
|
||||||
$menu = $el.find('.dropdown-menu'),
|
|
||||||
submenus = {},
|
|
||||||
ctxId = 1;
|
|
||||||
|
|
||||||
ctx = _.uniqueId(ctx + '_sub_');
|
|
||||||
|
|
||||||
// Sort by alphanumeric ordered first
|
|
||||||
items.sort(function(a, b) {
|
|
||||||
return a.label.localeCompare(b.label);
|
|
||||||
});
|
|
||||||
// Sort by priority
|
|
||||||
items.sort(function(a, b) {
|
|
||||||
return a.priority - b.priority;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let idx in items) {
|
|
||||||
m = items[idx];
|
|
||||||
$menu.append(m.$el);
|
|
||||||
if (!m.is_disabled) {
|
|
||||||
submenus[ctx + ctxId] = m.context;
|
|
||||||
}
|
|
||||||
ctxId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_disabled = (_.size(submenus) == 0);
|
|
||||||
|
|
||||||
return {
|
|
||||||
$el: $el,
|
|
||||||
priority: opts.priority || 10,
|
|
||||||
label: opts.label,
|
|
||||||
above: data['above'],
|
|
||||||
below: opts.below,
|
|
||||||
is_disabled: is_disabled,
|
|
||||||
context: {
|
|
||||||
name: opts.label,
|
|
||||||
icon: opts.icon,
|
|
||||||
items: submenus,
|
|
||||||
disabled: is_disabled,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A function to generate menus (submenus) based on the categories.
|
|
||||||
* Attach the current selected browser tree node to each of the generated
|
|
||||||
* menu-items.
|
|
||||||
*
|
|
||||||
* Arguments:
|
|
||||||
* 1. nodes_obj - Nodes object contains each node object.
|
|
||||||
* 2. jQuery Element on which you may want to created the menus
|
|
||||||
* 3. list of menu-items
|
|
||||||
* 4. categories - metadata information about the categories, based on which
|
|
||||||
* the submenu (menu-group) will be created (if any).
|
|
||||||
* 5. d - Data object for the selected browser tree item.
|
|
||||||
* 6. item - The selected browser tree item
|
|
||||||
* 7. menu_items - A empty object on which the context menu for the given
|
|
||||||
* list of menu-items.
|
|
||||||
*
|
|
||||||
* Returns if any menu generated for the given input.
|
|
||||||
*/
|
|
||||||
pgAdmin.Browser.MenuCreator = function(
|
|
||||||
nodes_obj, $mnu, menus, categories, d, item, menu_items
|
|
||||||
) {
|
|
||||||
let showMenu = true;
|
|
||||||
|
|
||||||
/* We check showMenu function is defined by the respective node, if it is
|
|
||||||
* defined then call the function which will return true or false.
|
|
||||||
*/
|
|
||||||
if (d && nodes_obj[d._type] && !_.isUndefined(nodes_obj[d._type].showMenu))
|
|
||||||
showMenu = nodes_obj[d._type].showMenu(d, item);
|
|
||||||
|
|
||||||
if (!showMenu) {
|
|
||||||
menu_items = menu_items || {};
|
|
||||||
menu_items[_.uniqueId('ctx_')+ '1_1_ms'] = {
|
|
||||||
disabled : true,
|
|
||||||
name: gettext('No menu available for this object.'),
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let groups = {
|
|
||||||
'common': [],
|
|
||||||
},
|
|
||||||
common, idx = 0,
|
|
||||||
ctxId = _.uniqueId('ctx_'),
|
|
||||||
update_menuitem = function(m) {
|
|
||||||
if (m instanceof MenuItem) {
|
|
||||||
if (m.$el) {
|
|
||||||
m.$el.remove();
|
|
||||||
delete m.$el;
|
|
||||||
}
|
|
||||||
m.generate(d, item);
|
|
||||||
let group = groups[m.category || 'common'] =
|
|
||||||
groups[m.category || 'common'] || [];
|
|
||||||
group.push(m);
|
|
||||||
} else if(m instanceof NewMenuItem) {
|
|
||||||
if (m.$el) {
|
|
||||||
m.$el.remove();
|
|
||||||
delete m.$el;
|
|
||||||
}
|
|
||||||
m.generate(this, self, this.context_menu_callback, item);
|
|
||||||
let group = groups[m.category || 'common'] =
|
|
||||||
groups[m.category || 'common'] || [];
|
|
||||||
group.push(m);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (let key in m) {
|
|
||||||
update_menuitem(m[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ctxIdx = 1;
|
|
||||||
|
|
||||||
for (idx in menus) {
|
|
||||||
update_menuitem(menus[idx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not all menu creator requires the context menu structure.
|
|
||||||
menu_items = menu_items || {};
|
|
||||||
|
|
||||||
common = groups['common'];
|
|
||||||
delete groups['common'];
|
|
||||||
|
|
||||||
let prev = true;
|
|
||||||
|
|
||||||
for (let name in groups) {
|
|
||||||
let g = groups[name],
|
|
||||||
c = categories[name] || {
|
|
||||||
'label': name,
|
|
||||||
single: false,
|
|
||||||
},
|
|
||||||
menu_group = pgAdmin.Browser.MenuGroup(c, g, prev, ctxId);
|
|
||||||
|
|
||||||
if (g.length <= 1 && !c.single) {
|
|
||||||
prev = false;
|
|
||||||
for (idx in g) {
|
|
||||||
common.push(g[idx]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
prev = g.below;
|
|
||||||
common.push(menu_group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The menus will be created based on the priority given.
|
|
||||||
// Menu with lowest value has the highest priority. If the priority is
|
|
||||||
// same, then - it will be ordered by label.
|
|
||||||
// Sort by alphanumeric ordered first
|
|
||||||
common.sort(function(a, b) {
|
|
||||||
return a.label.localeCompare(b.label);
|
|
||||||
});
|
|
||||||
// Sort by priority
|
|
||||||
common.sort(function(a, b) {
|
|
||||||
return a.priority - b.priority;
|
|
||||||
});
|
|
||||||
let len = _.size(common);
|
|
||||||
|
|
||||||
for (idx in common) {
|
|
||||||
item = common[idx];
|
|
||||||
|
|
||||||
item.priority = (item.priority || 10);
|
|
||||||
$mnu.append(item.$el);
|
|
||||||
let prefix = ctxId + '_' + item.priority + '_' + ctxIdx;
|
|
||||||
|
|
||||||
if (ctxIdx != 1 && item.above && !item.is_disabled) {
|
|
||||||
// For creatign the seprator any string will do.
|
|
||||||
menu_items[prefix + '_ma'] = '----';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!item.is_disabled) {
|
|
||||||
menu_items[prefix + '_ms'] = item.context;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctxId != len && item.below && !item.is_disabled) {
|
|
||||||
menu_items[prefix + '_mz'] = '----';
|
|
||||||
}
|
|
||||||
ctxIdx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (len > 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// MENU PUBLIC CLASS DEFINITION
|
|
||||||
// ==============================
|
|
||||||
let Menu = function(element, options) {
|
|
||||||
this.$element = $(element);
|
|
||||||
this.options = $.extend({}, Menu.DEFAULTS, options);
|
|
||||||
this.isLoading = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
Menu.DEFAULTS = {};
|
|
||||||
|
|
||||||
Menu.prototype.toggle = function(ev) {
|
|
||||||
let $parent = this.$element.closest('.dropdown-item');
|
|
||||||
if ($parent.hasClass('disabled')) {
|
|
||||||
ev.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let d = this.$element.data('pgMenu');
|
|
||||||
if (d.cb) {
|
|
||||||
let cb = d.module && d.module['callbacks'] && d.module['callbacks'][d.cb] || d.module && d.module[d.cb];
|
|
||||||
cb = cb || d.cb;
|
|
||||||
if (cb) {
|
|
||||||
cb.apply(d.module, [d.data, pgAdmin.Browser.tree.selected()]);
|
|
||||||
ev.preventDefault();
|
|
||||||
} else {
|
|
||||||
pgAdmin.Browser.report_error('Developer Warning: Callback - "' + d.cb + '" not found!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// BUTTON PLUGIN DEFINITION
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
function Plugin(option, ev) {
|
|
||||||
return this.each(function() {
|
|
||||||
let $this = $(this);
|
|
||||||
let data = $this.data('pg.menu');
|
|
||||||
let options = typeof option == 'object' && option;
|
|
||||||
|
|
||||||
if (!data) $this.data('pg.menu', (data = new Menu(this, options)));
|
|
||||||
|
|
||||||
data.toggle(ev);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let old = $.fn.button;
|
|
||||||
|
|
||||||
$.fn.pgmenu = Plugin;
|
|
||||||
$.fn.pgmenu.Constructor = Menu;
|
|
||||||
|
|
||||||
|
|
||||||
// BUTTON NO CONFLICT
|
|
||||||
// ==================
|
|
||||||
|
|
||||||
$.fn.pgmenu.noConflict = function() {
|
|
||||||
$.fn.pgmenu = old;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
// MENU DATA-API
|
|
||||||
// =============
|
|
||||||
|
|
||||||
$(document)
|
|
||||||
.on('click.pg.menu.data-api', '[data-toggle^="pg-menu"]', function(ev) {
|
|
||||||
let $menu = $(ev.target);
|
|
||||||
if (!$menu.hasClass('dropdown-item'))
|
|
||||||
$menu = $menu.closest('.dropdown-item');
|
|
||||||
Plugin.call($menu, 'toggle', ev);
|
|
||||||
})
|
|
||||||
.on(
|
|
||||||
'focus.pg.menu.data-api blur.pg.menu.data-api',
|
|
||||||
'[data-toggle^="pg-menu"]',
|
|
||||||
function(ev) {
|
|
||||||
$(ev.target).closest('.menu').toggleClass(
|
|
||||||
'focus', /^focus(in)?$/.test(ev.type)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.on('mouseenter', '.dropdown-submenu', function(ev) {
|
|
||||||
$(ev.currentTarget).removeClass('dropdown-submenu-visible')
|
|
||||||
.addClass('dropdown-submenu-visible');
|
|
||||||
$(ev.currentTarget).find('.dropdown-menu').first().addClass('show');
|
|
||||||
})
|
|
||||||
.on('mouseleave', '.dropdown-submenu', function(ev) {
|
|
||||||
$(ev.currentTarget).removeClass('dropdown-submenu-visible');
|
|
||||||
$(ev.currentTarget).find('.dropdown-menu').first().removeClass('show');
|
|
||||||
})
|
|
||||||
.on('hidden.bs.dropdown', function(ev) {
|
|
||||||
$(ev.target)
|
|
||||||
.find('.dropdown-submenu.dropdown-submenu-visible')
|
|
||||||
.removeClass('dropdown-submenu-visible')
|
|
||||||
.find('.dropdown-menu.show')
|
|
||||||
.removeClass('show');
|
|
||||||
});
|
|
||||||
|
|
||||||
return pgAdmin.Browser.MenuItem;
|
|
||||||
});
|
|
|
@ -6,8 +6,8 @@
|
||||||
// This software is released under the PostgreSQL Licence
|
// This software is released under the PostgreSQL Licence
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
import {MenuItem as NewMenuItem} from '../new_menu';
|
import {MenuItem as NewMenuItem} from '../../../../static/js/helpers/Menu';
|
||||||
import { MainMenus } from '../main_menu';
|
import { MainMenus } from '../MainMenuFactory';
|
||||||
import pgAdmin from 'sources/pgadmin';
|
import pgAdmin from 'sources/pgadmin';
|
||||||
import { getBrowser } from '../../../../static/js/utils';
|
import { getBrowser } from '../../../../static/js/utils';
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export function menuSearch(param, props) {
|
||||||
|
|
||||||
const iterItem = (subMenus, path, parentPath) => {
|
const iterItem = (subMenus, path, parentPath) => {
|
||||||
subMenus.forEach((subMenu) =>{
|
subMenus.forEach((subMenu) =>{
|
||||||
if(subMenu instanceof NewMenuItem || subMenu instanceof pgAdmin.Browser.MenuItem) {
|
if(subMenu instanceof NewMenuItem) {
|
||||||
if(subMenu.type != 'separator' && subMenu?.label?.toLowerCase().indexOf(param.toLowerCase()) != -1){
|
if(subMenu.type != 'separator' && subMenu?.label?.toLowerCase().indexOf(param.toLowerCase()) != -1){
|
||||||
let localPath = path;
|
let localPath = path;
|
||||||
if(parentPath) {
|
if(parentPath) {
|
||||||
|
@ -40,11 +40,11 @@ export function menuSearch(param, props) {
|
||||||
result.push(subMenu);
|
result.push(subMenu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(subMenu.menu_items) {
|
if(subMenu.getMenuItems()) {
|
||||||
iterItem(subMenu.menu_items, getMenuName(subMenu), path);
|
iterItem(subMenu.getMenuItems(), getMenuName(subMenu), path);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(typeof(subMenu) == 'object' && !(subMenu instanceof NewMenuItem || subMenu instanceof pgAdmin.Browser.MenuItem)) {
|
if(typeof(subMenu) == 'object' && !(subMenu instanceof NewMenuItem)) {
|
||||||
iterItem(Object.values(subMenu), path, parentPath);
|
iterItem(Object.values(subMenu), path, parentPath);
|
||||||
} else {
|
} else {
|
||||||
iterItem(subMenu, path, parentPath);
|
iterItem(subMenu, path, parentPath);
|
||||||
|
@ -67,10 +67,10 @@ export function menuSearch(param, props) {
|
||||||
if(menu.name == 'object') {
|
if(menu.name == 'object') {
|
||||||
let selectedNode = pgAdmin.Browser.tree.selected();
|
let selectedNode = pgAdmin.Browser.tree.selected();
|
||||||
if(selectedNode) {
|
if(selectedNode) {
|
||||||
subMenus = pgAdmin.Browser.menus[menu.name][selectedNode._metadata.data._type];
|
subMenus = pgAdmin.Browser.all_menus_cache[menu.name][selectedNode._metadata.data._type];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
subMenus = pgAdmin.Browser.menus[menu.name];
|
subMenus = pgAdmin.Browser.all_menus_cache[menu.name];
|
||||||
}
|
}
|
||||||
iterItem(Object.values(subMenus), getMenuName(menu));
|
iterItem(Object.values(subMenus), getMenuName(menu));
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% if config.SERVER_MODE and config.SHOW_GRAVATAR_IMAGE -%}
|
|
||||||
{% import 'browser/macros/gravatar_icon.macro' as IMG with context %}
|
|
||||||
{% elif config.SERVER_MODE %}
|
|
||||||
{% import 'browser/macros/static_user_icon.macro' as IMG with context %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% block title %}{{ config.APP_NAME }}{% endblock %}
|
{% block title %}{{ config.APP_NAME }}{% endblock %}
|
||||||
|
|
||||||
{% block init_script %}
|
{% block init_script %}
|
||||||
|
@ -74,17 +68,6 @@ require.onResourceLoad = function (context, map, depMaps) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
{% if config.SERVER_MODE %}
|
|
||||||
window.onload = function(e){
|
|
||||||
setTimeout(function() {
|
|
||||||
var gravatarImg = {{ IMG.PREPARE_HTML()|safe }}
|
|
||||||
var navbarUser = document.getElementById("navbar-user");
|
|
||||||
if (navbarUser) {
|
|
||||||
navbarUser.innerHTML = gravatarImg;
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block css_link %}
|
{% block css_link %}
|
||||||
|
@ -99,80 +82,7 @@ window.onload = function(e){
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if current_app.PGADMIN_RUNTIME | string() == 'False' %}
|
{% if current_app.PGADMIN_RUNTIME | string() == 'False' %}
|
||||||
<nav class="navbar fixed-top navbar-expand-lg navbar-dark pg-navbar">
|
<div id="main-menu-container"></div>
|
||||||
<a class="navbar-brand pgadmin_header_logo" onClick="return false;" href="{{ '#' }}"
|
|
||||||
title="{{ config.APP_NAME }} {{ _('logo') }}" aria-label="{ config.APP_NAME }} {{ _('logo') }}">
|
|
||||||
<i class="app-icon {{ config.APP_ICON }}" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
<button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#navbar-menu" aria-controls="navbar-menu">
|
|
||||||
<span class="sr-only">{{ _('Toggle navigation') }}</span>
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbar-menu" role="navigation">
|
|
||||||
|
|
||||||
<ul class="navbar-nav mr-auto">
|
|
||||||
<li id="mnu_file" class="nav-item active dropdown d-none">
|
|
||||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
|
||||||
_('File') }} <span class="caret"></span></a>
|
|
||||||
<ul class="dropdown-menu" role="menu"></ul>
|
|
||||||
</li>
|
|
||||||
<li id="mnu_obj" class="nav-item active dropdown ">
|
|
||||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
|
||||||
_('Object') }} <span class="caret"></span></a>
|
|
||||||
<ul class="dropdown-menu" role="menu"></ul>
|
|
||||||
</li>
|
|
||||||
<li id="mnu_management" class="nav-item active dropdown d-none">
|
|
||||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
|
||||||
_('Management') }} <span class="caret"></span></a>
|
|
||||||
<ul class="dropdown-menu" aria-hidden="true"></ul>
|
|
||||||
</li>
|
|
||||||
<li id="mnu_tools" class="nav-item active dropdown d-none">
|
|
||||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
|
||||||
_('Tools') }} <span class="caret"></span></a>
|
|
||||||
<ul class="dropdown-menu" role="menu"></ul>
|
|
||||||
</li>
|
|
||||||
<li id="mnu_help" class="nav-item active dropdown d-none">
|
|
||||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
|
||||||
_('Help') }} <span class="caret"></span></a>
|
|
||||||
<ul class="dropdown-menu help_menu" role="menu"></ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{% if config.SERVER_MODE %}
|
|
||||||
<ul class="navbar-nav">
|
|
||||||
<li class="nav-item active dropdown">
|
|
||||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown"
|
|
||||||
role="button" aria-expanded="false" id="navbar-user"></a>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
|
||||||
{% if auth_only_internal %}
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item" href="#" role="menuitem" onclick="pgAdmin.UserManagement.change_password(
|
|
||||||
'{{ url_for('browser.change_password') }}'
|
|
||||||
)">
|
|
||||||
{{ _('Change Password') }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="dropdown-divider"></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if mfa_enabled is defined and mfa_enabled is true %}
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item" href="#" role="menuitem" onclick="pgAdmin.UserManagement.show_mfa(
|
|
||||||
'{{ login_url("mfa.register", next_url="internal") }}'
|
|
||||||
)">{{ _('Two-Factor Authentication') }}</a>
|
|
||||||
</li>
|
|
||||||
<li class="dropdown-divider"></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if is_admin %}
|
|
||||||
<li><a class="dropdown-item" href="#" role="menuitem" onclick="pgAdmin.UserManagement.show_users()">{{ _('Users') }}</a></li>
|
|
||||||
<li class="dropdown-divider"></li>
|
|
||||||
{% endif %}
|
|
||||||
<li><a class="dropdown-item" role="menuitem" href="{{ logout_url }}">{{ _('Logout') }}</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div id="dockerContainer" class="pg-docker"></div>
|
<div id="dockerContainer" class="pg-docker"></div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div id="dockerContainer" class="pg-docker pg-docker-native"></div>
|
<div id="dockerContainer" class="pg-docker pg-docker-native"></div>
|
||||||
|
|
|
@ -95,26 +95,74 @@ define('pgadmin.browser.utils',
|
||||||
// after they all were loaded completely.
|
// after they all were loaded completely.
|
||||||
},
|
},
|
||||||
|
|
||||||
addMenus: function (obj) {
|
addBackendMenus: function (obj) {
|
||||||
// Generate the menu items only when all the initial scripts
|
// Generate the menu items only when all the initial scripts
|
||||||
// were loaded completely.
|
// were loaded completely.
|
||||||
//
|
//
|
||||||
// First - register the menus from the other
|
// First - register the menus from the other
|
||||||
// modules/extensions.
|
// modules/extensions.
|
||||||
let self = this;
|
{% for key in ('File', 'Edit', 'Object' 'Tools', 'Management', 'Help') %}
|
||||||
if (this.counter.total == this.counter.loaded) {
|
obj.add_menus({{ MENU_ITEMS(key, current_app.menu_items['%s_items' % key.lower()])}});
|
||||||
{% for key in ('File', 'Edit', 'Object' 'Tools', 'Management', 'Help') %}
|
{% endfor %}
|
||||||
obj.add_menus({{ MENU_ITEMS(key, current_app.menu_items['%s_items' % key.lower()])}});
|
|
||||||
{% endfor %}
|
|
||||||
if('{{current_app.PGADMIN_RUNTIME}}' == 'False') {
|
|
||||||
obj.create_menus();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//recall after some time
|
|
||||||
setTimeout(function(){ self.addMenus(obj); }, 3000);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{% if current_app.config.get('SERVER_MODE') %}
|
||||||
|
userMenuInfo: {
|
||||||
|
username: '{{username}}',
|
||||||
|
auth_source: '{{auth_source}}',
|
||||||
|
gravatar: {% if config.SHOW_GRAVATAR_IMAGE %}'{{ username | gravatar }}'{% else %}''{% endif %},
|
||||||
|
menus: [
|
||||||
|
{% if auth_only_internal %}
|
||||||
|
{
|
||||||
|
label: '{{ _('Change Password') }}',
|
||||||
|
type: 'normal',
|
||||||
|
callback: ()=>{
|
||||||
|
pgAdmin.UserManagement.change_password(
|
||||||
|
'{{ url_for('browser.change_password') }}'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator',
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
|
{% if mfa_enabled is defined and mfa_enabled is true %}
|
||||||
|
{
|
||||||
|
label: '{{ _('Two-Factor Authentication') }}',
|
||||||
|
type: 'normal',
|
||||||
|
callback: ()=>{
|
||||||
|
pgAdmin.UserManagement.show_mfa(
|
||||||
|
'{{ login_url("mfa.register", next_url="internal") }}'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator',
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
|
{% if is_admin %}
|
||||||
|
{
|
||||||
|
label: '{{ _('Users') }}',
|
||||||
|
type: 'normal',
|
||||||
|
callback: ()=>{
|
||||||
|
pgAdmin.UserManagement.show_users()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator',
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
|
{
|
||||||
|
label: '{{ _('Logout') }}',
|
||||||
|
type: 'normal',
|
||||||
|
callback: ()=>{
|
||||||
|
window.location="{{ logout_url }}";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
// load the module right now
|
// load the module right now
|
||||||
load_module: function(name, path, c) {
|
load_module: function(name, path, c) {
|
||||||
let obj = this;
|
let obj = this;
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
{##########################################################################
|
|
||||||
We wrote separate macro because if user choose to disable Gravatar then
|
|
||||||
we will not associate our application with Gravatar module which will make
|
|
||||||
'gravatar' filter unavailable in Jinja templates
|
|
||||||
###########################################################################}
|
|
||||||
{% macro PREPARE_HTML() -%}
|
|
||||||
'<img src = "{{ username | gravatar }}" width = "18" height = "18" alt = "Gravatar image for {{ username }}" > {{ username }} ({{auth_source}}) <span class="caret"></span>';
|
|
||||||
{%- endmacro %}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{% macro PREPARE_HTML() -%}
|
|
||||||
'<i class="fa fa-user-circle pg-login-icon" aria-hidden="true"></i> {{ username }} <span class="caret"></span>';
|
|
||||||
{%- endmacro %}
|
|
|
@ -7,6 +7,12 @@
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import MainMenuFactory from '../../browser/static/js/MainMenuFactory';
|
||||||
|
import AppMenuBar from '../js/AppMenuBar';
|
||||||
|
import Theme from '../js/Theme';
|
||||||
|
|
||||||
define('app', [
|
define('app', [
|
||||||
'sources/pgadmin', 'bundled_browser',
|
'sources/pgadmin', 'bundled_browser',
|
||||||
], function(pgAdmin) {
|
], function(pgAdmin) {
|
||||||
|
@ -38,6 +44,14 @@ define('app', [
|
||||||
initializeModules(pgAdmin.Browser);
|
initializeModules(pgAdmin.Browser);
|
||||||
initializeModules(pgAdmin.Tools);
|
initializeModules(pgAdmin.Tools);
|
||||||
|
|
||||||
// create menus after all modules are initialized.
|
// Add menus from back end.
|
||||||
pgAdmin.Browser.create_menus();
|
pgAdmin.Browser.utils.addBackendMenus(pgAdmin.Browser);
|
||||||
|
|
||||||
|
// Create menus after all modules are initialized.
|
||||||
|
MainMenuFactory.createMainMenus();
|
||||||
|
|
||||||
|
const menuContainerEle = document.querySelector('#main-menu-container');
|
||||||
|
if(menuContainerEle) {
|
||||||
|
ReactDOM.render(<Theme><AppMenuBar /></Theme>, document.querySelector('#main-menu-container'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
import { Box, makeStyles } from '@material-ui/core';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { PrimaryButton } from './components/Buttons';
|
||||||
|
import { PgMenu, PgMenuDivider, PgMenuItem, PgSubMenu } from './components/Menu';
|
||||||
|
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
|
||||||
|
import AccountCircleRoundedIcon from '@material-ui/icons/AccountCircleRounded';
|
||||||
|
import pgAdmin from 'sources/pgadmin';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme)=>({
|
||||||
|
root: {
|
||||||
|
height: '32px',
|
||||||
|
backgroundColor: theme.palette.primary.main,
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
padding: '0 0.5rem',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
width: '96px',
|
||||||
|
height: '100%',
|
||||||
|
/*
|
||||||
|
* Using the SVG postgresql logo, modified to set the background color as #FFF
|
||||||
|
* https://wiki.postgresql.org/images/9/90/PostgreSQL_logo.1color_blue.svg
|
||||||
|
* background: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42 42' style='enable-background:new 0 0 42 42;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bstroke:%23000000;stroke-width:3.3022;%7D .st1%7Bfill:%23336791;%7D .st2%7Bfill:none;stroke:%23FFFFFF;stroke-width:1.1007;stroke-linecap:round;stroke-linejoin:round;%7D .st3%7Bfill:none;stroke:%23FFFFFF;stroke-width:1.1007;stroke-linecap:round;stroke-linejoin:bevel;%7D .st4%7Bfill:%23FFFFFF;stroke:%23FFFFFF;stroke-width:0.3669;%7D .st5%7Bfill:%23FFFFFF;stroke:%23FFFFFF;stroke-width:0.1835;%7D .st6%7Bfill:none;stroke:%23FFFFFF;stroke-width:0.2649;stroke-linecap:round;stroke-linejoin:round;%7D%0A%3C/style%3E%3Cg id='orginal'%3E%3C/g%3E%3Cg id='Layer_x0020_3'%3E%3Cpath class='st0' d='M31.3,30c0.3-2.1,0.2-2.4,1.7-2.1l0.4,0c1.2,0.1,2.8-0.2,3.7-0.6c2-0.9,3.1-2.4,1.2-2 c-4.4,0.9-4.7-0.6-4.7-0.6c4.7-7,6.7-15.8,5-18c-4.6-5.9-12.6-3.1-12.7-3l0,0c-0.9-0.2-1.9-0.3-3-0.3c-2,0-3.5,0.5-4.7,1.4 c0,0-14.3-5.9-13.6,7.4c0.1,2.8,4,21.3,8.7,15.7c1.7-2,3.3-3.8,3.3-3.8c0.8,0.5,1.8,0.8,2.8,0.7l0.1-0.1c0,0.3,0,0.5,0,0.8 c-1.2,1.3-0.8,1.6-3.2,2.1c-2.4,0.5-1,1.4-0.1,1.6c1.1,0.3,3.7,0.7,5.5-1.8l-0.1,0.3c0.5,0.4,0.4,2.7,0.5,4.4 c0.1,1.7,0.2,3.2,0.5,4.1c0.3,0.9,0.7,3.3,3.9,2.6C29.1,38.3,31.1,37.5,31.3,30'/%3E%3Cpath class='st1' d='M38.3,25.3c-4.4,0.9-4.7-0.6-4.7-0.6c4.7-7,6.7-15.8,5-18c-4.6-5.9-12.6-3.1-12.7-3l0,0 c-0.9-0.2-1.9-0.3-3-0.3c-2,0-3.5,0.5-4.7,1.4c0,0-14.3-5.9-13.6,7.4c0.1,2.8,4,21.3,8.7,15.7c1.7-2,3.3-3.8,3.3-3.8 c0.8,0.5,1.8,0.8,2.8,0.7l0.1-0.1c0,0.3,0,0.5,0,0.8c-1.2,1.3-0.8,1.6-3.2,2.1c-2.4,0.5-1,1.4-0.1,1.6c1.1,0.3,3.7,0.7,5.5-1.8 l-0.1,0.3c0.5,0.4,0.8,2.4,0.7,4.3c-0.1,1.9-0.1,3.2,0.3,4.2c0.4,1,0.7,3.3,3.9,2.6c2.6-0.6,4-2,4.2-4.5c0.1-1.7,0.4-1.5,0.5-3 l0.2-0.7c0.3-2.3,0-3.1,1.7-2.8l0.4,0c1.2,0.1,2.8-0.2,3.7-0.6C39,26.4,40.2,24.9,38.3,25.3L38.3,25.3z'/%3E%3Cpath class='st2' d='M21.8,26.6c-0.1,4.4,0,8.8,0.5,9.8c0.4,1.1,1.3,3.2,4.5,2.5c2.6-0.6,3.6-1.7,4-4.1c0.3-1.8,0.9-6.7,1-7.7'/%3E%3Cpath class='st2' d='M18,4.7c0,0-14.3-5.8-13.6,7.4c0.1,2.8,4,21.3,8.7,15.7c1.7-2,3.2-3.7,3.2-3.7'/%3E%3Cpath class='st2' d='M25.7,3.6c-0.5,0.2,7.9-3.1,12.7,3c1.7,2.2-0.3,11-5,18'/%3E%3Cpath class='st3' d='M33.5,24.6c0,0,0.3,1.5,4.7,0.6c1.9-0.4,0.8,1.1-1.2,2c-1.6,0.8-5.3,0.9-5.3-0.1 C31.6,24.5,33.6,25.3,33.5,24.6c-0.1-0.6-1.1-1.2-1.7-2.7c-0.5-1.3-7.3-11.2,1.9-9.7c0.3-0.1-2.4-8.7-11-8.9 c-8.6-0.1-8.3,10.6-8.3,10.6'/%3E%3Cpath class='st2' d='M19.4,25.6c-1.2,1.3-0.8,1.6-3.2,2.1c-2.4,0.5-1,1.4-0.1,1.6c1.1,0.3,3.7,0.7,5.5-1.8c0.5-0.8,0-2-0.7-2.3 C20.5,25.1,20,24.9,19.4,25.6L19.4,25.6z'/%3E%3Cpath class='st2' d='M19.3,25.5c-0.1-0.8,0.3-1.7,0.7-2.8c0.6-1.6,2-3.3,0.9-8.5c-0.8-3.9-6.5-0.8-6.5-0.3c0,0.5,0.3,2.7-0.1,5.2 c-0.5,3.3,2.1,6,5,5.7'/%3E%3Cpath class='st4' d='M18,13.8c0,0.2,0.3,0.7,0.8,0.7c0.5,0.1,0.9-0.3,0.9-0.5c0-0.2-0.3-0.4-0.8-0.4C18.4,13.6,18,13.7,18,13.8 L18,13.8z'/%3E%3Cpath class='st5' d='M32,13.5c0,0.2-0.3,0.7-0.8,0.7c-0.5,0.1-0.9-0.3-0.9-0.5c0-0.2,0.3-0.4,0.8-0.4C31.6,13.2,32,13.3,32,13.5 L32,13.5z'/%3E%3Cpath class='st2' d='M33.7,12.2c0.1,1.4-0.3,2.4-0.4,3.9c-0.1,2.2,1,4.7-0.6,7.2'/%3E%3Cpath class='st6' d='M2.7,6.6'/%3E%3C/g%3E%3C/svg%3E%0A") 0 0 no-repeat;
|
||||||
|
*/
|
||||||
|
background: 'url(data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDUgNTAiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojZmZmO30uY2xzLTJ7ZmlsbDojMzI2ODkzO308L3N0eWxlPjwvZGVmcz48dGl0bGU+cGdBZG1pbjwvdGl0bGU+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNTguOTQsNDEuNGEyLjQ4LDIuNDgsMCwwLDEtMi4yNy0zLjQ5TDY0LDIxLjI5VjZhNiw2LDAsMCwwLTYtNkg2QTYsNiwwLDAsMCwwLDZWNDRhNiw2LDAsMCwwLDYsNkg1OGE2LDYsMCwwLDAsNi02VjQxLjRaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMjkuMjUsMzAuMTdhMTMuMTMsMTMuMTMsMCwwLDEtMS44Mi02LjkzLDEzLDEzLDAsMCwxLDEuODItNi44OCwxMi41LDEyLjUsMCwwLDEsMS40OC0xLjk1LDEwLjQ0LDEwLjQ0LDAsMCwwLTMuMjUtMi44OSwxMS4xNiwxMS4xNiwwLDAsMC01LjY1LTEuNDVxLTQuNDgsMC02LjcyLDIuNjRWMTAuNDRINy41MVY0MC4zNmExLDEsMCwwLDAsMSwxaDZhMSwxLDAsMCwwLDEtMVYzMS4xOWE4LjQ3LDguNDcsMCwwLDAsNi4zNCwyLjQsMTEuMjYsMTEuMjYsMCwwLDAsNS42NS0xLjQ1LDEwLjUzLDEwLjUzLDAsMCwwLDIuMDYtMS41NkMyOS40NCwzMC40NCwyOS4zNCwzMC4zMSwyOS4yNSwzMC4xN1pNMjMuNiwyNS44YTQuNTIsNC41MiwwLDAsMS0zLjQ1LDEuNDQsNC40OCw0LjQ4LDAsMCwxLTMuNDQtMS40NCw1LjYsNS42LDAsMCwxLTEuMzUtNCw1LjU5LDUuNTksMCwwLDEsMS4zNS00LDQuNDYsNC40NiwwLDAsMSwzLjQ0LTEuNDUsNC40OSw0LjQ5LDAsMCwxLDMuNDUsMS40NSw1LjYzLDUuNjMsMCwwLDEsMS4zNCw0QTUuNjQsNS42NCwwLDAsMSwyMy42LDI1LjhaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNNTYuNDksMTIuNjNWMzEuMjRxMCw2LjM1LTMuNDQsOS41MXQtOS45MiwzLjE3YTI1LjQyLDI1LjQyLDAsMCwxLTYuMy0uNzUsMTUsMTUsMCwwLDEtNS0yLjIzbDIuODktNS41OWExMC4xNywxMC4xNywwLDAsMCwzLjUxLDEuNzksMTQuMzcsMTQuMzcsMCwwLDAsNC4xOC42NUE2LjUzLDYuNTMsMCwwLDAsNDcsMzYuNGE1LjM3LDUuMzcsMCwwLDAsMS40Ny00LjExdi0uNzZjLTEuNTQsMS44LTMuNzksMi42OS02Ljc2LDIuNjlhMTEuNywxMS43LDAsMCwxLTUuNTktMS4zNkExMC4zNywxMC4zNywwLDAsMSwzMi4wOSwyOWExMC44OSwxMC44OSwwLDAsMS0xLjUxLTUuNzcsMTAuODYsMTAuODYsMCwwLDEsMS41MS01Ljc0LDEwLjQyLDEwLjQyLDAsMCwxLDQuMDctMy44NiwxMS43MSwxMS43MSwwLDAsMSw1LjU5LTEuMzdjMy4yNSwwLDUuNjMsMS4wNiw3LjE0LDMuMTVWMTIuNjNabS05LjMsMTMuOTVhNC40LDQuNCwwLDAsMCwxLjQtMy4zNiw0LjM0LDQuMzQsMCwwLDAtMS4zOC0zLjM0LDUuNjUsNS42NSwwLDAsMC03LjE2LDAsNC4zLDQuMywwLDAsMC0xLjQxLDMuMzQsNC4zNSw0LjM1LDAsMCwwLDEuNDMsMy4zNiw1LjA4LDUuMDgsMCwwLDAsMy41NywxLjNBNSw1LDAsMCwwLDQ3LjE5LDI2LjU4WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTgzLjQzLDMyLjg5SDcxbC0yLDUuMDlhMSwxLDAsMCwxLS45My42Mkg2MS43M2ExLDEsMCwwLDEtLjkxLTEuNEw3Mi45MSw5LjhhMSwxLDAsMCwxLC45Mi0uNmg2Ljg5YTEsMSwwLDAsMSwuOTEuNkw5My43NywzNy4yYTEsMSwwLDAsMS0uOTIsMS40SDg2LjQxYTEsMSwwLDAsMS0uOTMtLjYyWk04MSwyNi43NmwtMy43OC05LjQxLTMuNzgsOS40MVoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xMjAuNDQsOC40NFYzNy42YTEsMSwwLDAsMS0xLDFoLTUuNmExLDEsMCwwLDEtMS0xVjM2LjMzUTExMC42MiwzOSwxMDYuMTYsMzlhMTEuMjksMTEuMjksMCwwLDEtNS42Ny0xLjQ1LDEwLjU0LDEwLjU0LDAsMCwxLTQtNC4xNEExMi42MiwxMi42MiwwLDAsMSw5NSwyNy4xOCwxMi41MywxMi41MywwLDAsMSw5Ni40NCwyMWExMC4zNSwxMC4zNSwwLDAsMSw0LTQuMDksMTEuNDgsMTEuNDgsMCwwLDEsNS42Ny0xLjQzLDguMjQsOC4yNCwwLDAsMSw2LjMsMi4zNVY4LjQ0YTEsMSwwLDAsMSwxLTFoNkExLDEsMCwwLDEsMTIwLjQ0LDguNDRabS05LjE5LDIyLjc1YTUuNzEsNS43MSwwLDAsMCwxLjM0LTQsNS42LDUuNiwwLDAsMC0xLjMyLTMuOTUsNC40Nyw0LjQ3LDAsMCwwLTMuNDMtMS40Myw0LjUzLDQuNTMsMCwwLDAtMy40NCwxLjQzLDUuNTEsNS41MSwwLDAsMC0xLjM0LDMuOTUsNS42Nyw1LjY3LDAsMCwwLDEuMzQsNCw0Ljc3LDQuNzcsMCwwLDAsNi44NSwwWiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTE2MSwxOGMxLjY2LDEuNjgsMi41LDQuMjEsMi41LDcuNnYxMmExLDEsMCwwLDEtMSwxaC02YTEsMSwwLDAsMS0xLTFWMjYuODhhNS42Nyw1LjY3LDAsMCwwLS45LTMuNTMsMy4wOSwzLjA5LDAsMCwwLTIuNTUtMS4xMywzLjYyLDMuNjIsMCwwLDAtMi44OSwxLjI2LDUuNzEsNS43MSwwLDAsMC0xLjEsMy44MlYzNy42YTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYyNi44OGMwLTMuMTEtMS4xNC00LjY2LTMuNDQtNC42NmEzLjcsMy43LDAsMCwwLTIuOTQsMS4yNiw1LjcxLDUuNzEsMCwwLDAtMS4wOSwzLjgyVjM3LjZhMSwxLDAsMCwxLTEsMWgtNmExLDEsMCwwLDEtMS0xVjE2Ljg0YTEsMSwwLDAsMSwxLTFoNS42YTEsMSwwLDAsMSwxLDF2MS4zOWE4LDgsMCwwLDEsMy0yLjA4LDEwLjIzLDEwLjIzLDAsMCwxLDMuOC0uNjksMTAsMTAsMCwwLDEsNC4yOS44OEE3LjI4LDcuMjgsMCwwLDEsMTQ2LjQyLDE5YTguODUsOC44NSwwLDAsMSwzLjQxLTIuNjUsMTAuOTMsMTAuOTMsMCwwLDEsNC40OS0uOTJBOSw5LDAsMCwxLDE2MSwxOFoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xNjguMTIsMTIuMWEzLjkxLDMuOTEsMCwwLDEtMS4zNC0yLjc5QTQuMTYsNC4xNiwwLDAsMSwxNjgsNi4xOWE1LDUsMCwwLDEsMy42Ny0xLjM2QTUuMjUsNS4yNSwwLDAsMSwxNzUuMTgsNmEzLjc1LDMuNzUsMCwwLDEsMS4zNCwzLDQuMSw0LjEsMCwwLDEtMS4zNCwzLjEzLDUuNjgsNS42OCwwLDAsMS03LjA2LDBabS41NCwzLjc0aDZhMSwxLDAsMCwxLDEsMVYzNy42YTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYxNi44NEExLDEsMCwwLDEsMTY4LjY2LDE1Ljg0WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTIwMS41NSwxOHEyLjU5LDIuNTIsMi41OSw3LjZ2MTJhMSwxLDAsMCwxLTEsMWgtNmExLDEsMCwwLDEtMS0xVjI2Ljg4cTAtNC42Ni0zLjc0LTQuNjZhNC4zLDQuMywwLDAsMC0zLjMsMS4zNCw1LjgzLDUuODMsMCwwLDAtMS4yNCw0djEwYTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYxNi44NGExLDEsMCwwLDEsMS0xaDUuNjFhMSwxLDAsMCwxLDEsMXYxLjQ3YTkuMDUsOS4wNSwwLDAsMSwzLjE5LTIuMTIsMTAuNzgsMTAuNzgsMCwwLDEsNC0uNzNBOS4zNCw5LjM0LDAsMCwxLDIwMS41NSwxOFoiLz48L3N2Zz4=) 0 0 no-repeat',
|
||||||
|
backgroundPositionY: 'center',
|
||||||
|
},
|
||||||
|
menus: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '2px',
|
||||||
|
marginLeft: '16px',
|
||||||
|
|
||||||
|
'& .MuiButton-containedPrimary': {
|
||||||
|
padding: '2px 8px',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
menuButton: {
|
||||||
|
fontSize: '0.925rem',
|
||||||
|
},
|
||||||
|
userMenu: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
'& .MuiButton-containedPrimary': {
|
||||||
|
fontSize: '0.825rem',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gravatar: {
|
||||||
|
marginRight: '4px',
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function AppMenuBar() {
|
||||||
|
const classes = useStyles();
|
||||||
|
const [,setRefresh] = useState(false);
|
||||||
|
|
||||||
|
const reRenderMenus = ()=>setRefresh((prev)=>!prev);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
pgAdmin.Browser.Events.on('pgadmin:nw-enable-disable-menu-items', ()=>{
|
||||||
|
reRenderMenus();
|
||||||
|
});
|
||||||
|
pgAdmin.Browser.Events.on('pgadmin:nw-refresh-menu-item', ()=>{
|
||||||
|
reRenderMenus();
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getPgMenuItem = (menuItem, i)=>{
|
||||||
|
if(menuItem.type == 'separator') {
|
||||||
|
return <PgMenuDivider key={i}/>;
|
||||||
|
}
|
||||||
|
const hasCheck = typeof menuItem.checked == 'boolean';
|
||||||
|
|
||||||
|
return <PgMenuItem
|
||||||
|
key={i}
|
||||||
|
disabled={menuItem.isDisabled}
|
||||||
|
onClick={()=>{
|
||||||
|
menuItem.callback();
|
||||||
|
if(hasCheck) {
|
||||||
|
reRenderMenus();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
hasCheck={hasCheck}
|
||||||
|
checked={menuItem.checked}
|
||||||
|
>{menuItem.label}</PgMenuItem>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const userMenuInfo = pgAdmin.Browser.utils.userMenuInfo;
|
||||||
|
|
||||||
|
return(
|
||||||
|
<>
|
||||||
|
<Box className={classes.root}>
|
||||||
|
<div className={classes.logo} />
|
||||||
|
<div className={classes.menus}>
|
||||||
|
{pgAdmin.Browser.MainMenus?.map((menu, i)=>{
|
||||||
|
return (
|
||||||
|
<PgMenu
|
||||||
|
menuButton={<PrimaryButton key={i} className={classes.menuButton} data-label={menu.label}>{menu.label}<KeyboardArrowDownIcon fontSize="small" /></PrimaryButton>}
|
||||||
|
label={menu.label}
|
||||||
|
key={menu.name}
|
||||||
|
>
|
||||||
|
{menu.getMenuItems().map((menuItem, i)=>{
|
||||||
|
const submenus = menuItem.getMenuItems();
|
||||||
|
if(submenus) {
|
||||||
|
return <PgSubMenu key={i} label={menuItem.label}>
|
||||||
|
{submenus.map((submenuItem, si)=>{
|
||||||
|
return getPgMenuItem(submenuItem, si);
|
||||||
|
})}
|
||||||
|
</PgSubMenu>;
|
||||||
|
}
|
||||||
|
return getPgMenuItem(menuItem, i);
|
||||||
|
})}
|
||||||
|
</PgMenu>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{userMenuInfo &&
|
||||||
|
<div className={classes.userMenu}>
|
||||||
|
<PgMenu
|
||||||
|
menuButton={
|
||||||
|
<PrimaryButton className={classes.menuButton} data-test="loggedin-username">
|
||||||
|
<div className={classes.gravatar}>
|
||||||
|
{userMenuInfo.gravatar &&
|
||||||
|
<img src={userMenuInfo.gravatar} width = "18" height = "18"
|
||||||
|
alt = "Gravatar image for {{ username }}" />}
|
||||||
|
{!userMenuInfo.gravatar && <AccountCircleRoundedIcon />}
|
||||||
|
</div>
|
||||||
|
{ userMenuInfo.username } ({userMenuInfo.auth_source})
|
||||||
|
<KeyboardArrowDownIcon fontSize="small" />
|
||||||
|
</PrimaryButton>
|
||||||
|
}
|
||||||
|
label={userMenuInfo.username}
|
||||||
|
align="end"
|
||||||
|
>
|
||||||
|
{userMenuInfo.menus.map((menuItem, i)=>{
|
||||||
|
return getPgMenuItem(menuItem, i);
|
||||||
|
})}
|
||||||
|
</PgMenu>
|
||||||
|
</div>}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -88,6 +88,7 @@ basicSettings = createMuiTheme(basicSettings, {
|
||||||
root: {
|
root: {
|
||||||
textTransform: 'none',
|
textTransform: 'none',
|
||||||
padding: basicSettings.spacing(0.5, 1.5),
|
padding: basicSettings.spacing(0.5, 1.5),
|
||||||
|
fontSize: 'inherit',
|
||||||
'&.Mui-disabled': {
|
'&.Mui-disabled': {
|
||||||
opacity: 0.60,
|
opacity: 0.60,
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,6 +7,8 @@ import {
|
||||||
MenuItem,
|
MenuItem,
|
||||||
ControlledMenu,
|
ControlledMenu,
|
||||||
applyStatics,
|
applyStatics,
|
||||||
|
Menu,
|
||||||
|
SubMenu,
|
||||||
} from '@szhsin/react-menu';
|
} from '@szhsin/react-menu';
|
||||||
export {MenuDivider as PgMenuDivider} from '@szhsin/react-menu';
|
export {MenuDivider as PgMenuDivider} from '@szhsin/react-menu';
|
||||||
import { shortcutToString } from './ShortcutTitle';
|
import { shortcutToString } from './ShortcutTitle';
|
||||||
|
@ -25,14 +27,14 @@ const useStyles = makeStyles((theme)=>({
|
||||||
'& .szh-menu__divider': {
|
'& .szh-menu__divider': {
|
||||||
margin: 0,
|
margin: 0,
|
||||||
background: theme.otherVars.borderColor,
|
background: theme.otherVars.borderColor,
|
||||||
}
|
},
|
||||||
},
|
'& .szh-menu__item': {
|
||||||
menuItem: {
|
display: 'flex',
|
||||||
display: 'flex',
|
padding: '4px 8px',
|
||||||
padding: '4px 8px',
|
'&.szh-menu__item--active, &.szh-menu__item--hover': {
|
||||||
'&.szh-menu__item--active, &.szh-menu__item--hover': {
|
backgroundColor: theme.palette.primary.main,
|
||||||
backgroundColor: theme.palette.primary.main,
|
color: theme.palette.primary.contrastText,
|
||||||
color: theme.palette.primary.contrastText,
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkIcon: {
|
checkIcon: {
|
||||||
|
@ -48,10 +50,19 @@ const useStyles = makeStyles((theme)=>({
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export function PgMenu({open, className, label, ...props}) {
|
export function PgMenu({open, className, label, menuButton, ...props}) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const state = open ? 'open' : 'closed';
|
const state = open ? 'open' : 'closed';
|
||||||
props.anchorRef?.current?.setAttribute('data-state', state);
|
props.anchorRef?.current?.setAttribute('data-state', state);
|
||||||
|
|
||||||
|
if(menuButton) {
|
||||||
|
return <Menu
|
||||||
|
{...props}
|
||||||
|
menuButton={menuButton}
|
||||||
|
className={clsx(classes.menu, className)}
|
||||||
|
aria-label={label || 'Menu'}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<ControlledMenu
|
<ControlledMenu
|
||||||
state={state}
|
state={state}
|
||||||
|
@ -68,8 +79,15 @@ PgMenu.propTypes = {
|
||||||
className: CustomPropTypes.className,
|
className: CustomPropTypes.className,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
anchorRef: CustomPropTypes.ref,
|
anchorRef: CustomPropTypes.ref,
|
||||||
|
menuButton: PropTypes.oneOfType([React.ReactNode, undefined]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PgSubMenu = applyStatics(SubMenu)(({label, ...props})=>{
|
||||||
|
return (
|
||||||
|
<SubMenu label={label} itemProps={{'data-label': label}} {...props} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export const PgMenuItem = applyStatics(MenuItem)(({hasCheck=false, checked=false, accesskey, shortcut, children, ...props})=>{
|
export const PgMenuItem = applyStatics(MenuItem)(({hasCheck=false, checked=false, accesskey, shortcut, children, ...props})=>{
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
let onClick = props.onClick;
|
let onClick = props.onClick;
|
||||||
|
@ -80,7 +98,7 @@ export const PgMenuItem = applyStatics(MenuItem)(({hasCheck=false, checked=false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const dataLabel = typeof(children) == 'string' ? children : undefined;
|
const dataLabel = typeof(children) == 'string' ? children : undefined;
|
||||||
return <MenuItem {...props} onClick={onClick} className={classes.menuItem} data-label={dataLabel} data-checked={checked}>
|
return <MenuItem {...props} onClick={onClick} data-label={dataLabel} data-checked={checked}>
|
||||||
{hasCheck && <CheckIcon className={classes.checkIcon} style={checked ? {} : {visibility: 'hidden'}} />}
|
{hasCheck && <CheckIcon className={classes.checkIcon} style={checked ? {} : {visibility: 'hidden'}} />}
|
||||||
{children}
|
{children}
|
||||||
{(shortcut || accesskey) && <div className={classes.shortcut}>({shortcutToString(shortcut, accesskey)})</div>}
|
{(shortcut || accesskey) && <div className={classes.shortcut}>({shortcutToString(shortcut, accesskey)})</div>}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import gettext from 'sources/gettext';
|
||||||
export default class Menu {
|
export default class Menu {
|
||||||
constructor(name, label, id, index, addSepratior) {
|
constructor(name, label, id, index, addSepratior) {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.index = index || 1;
|
this.index = index || 1;
|
||||||
this.menuItems = [],
|
this.menuItems = [],
|
||||||
|
@ -102,42 +102,11 @@ export default class Menu {
|
||||||
getMenuItems() {
|
getMenuItems() {
|
||||||
return this.menuItems;
|
return this.menuItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getContextMenus(menuList, item, node) {
|
|
||||||
Menu.sortMenus(menuList);
|
|
||||||
|
|
||||||
let ctxMenus = {};
|
|
||||||
let ctxIndex = 1;
|
|
||||||
menuList.forEach(ctx => {
|
|
||||||
let ctx_uid = _.uniqueId('ctx_');
|
|
||||||
let sub_ctx_item = {};
|
|
||||||
ctx.is_disabled = ctx.disabled(node, item);
|
|
||||||
if ('menu_items' in ctx && ctx.menu_items) {
|
|
||||||
Menu.sortMenus(ctx.menu_items);
|
|
||||||
ctx.menu_items.forEach((c) => {
|
|
||||||
c.is_disabled = c.disabled(node, item);
|
|
||||||
if (!c.is_disabled) {
|
|
||||||
sub_ctx_item[ctx_uid + _.uniqueId('_sub_')] = c.getContextItem(c.label, c.is_disabled);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!ctx.is_disabled) {
|
|
||||||
ctxMenus[ctx_uid + '_' + ctx.priority + '_' + + ctxIndex + '_itm'] = ctx.getContextItem(ctx.label, ctx.is_disabled, sub_ctx_item);
|
|
||||||
|
|
||||||
if (_.size(sub_ctx_item) > 0 && ['register', 'create'].includes(ctx.category)) {
|
|
||||||
ctxMenus[ctx_uid + '_' + ctx.priority + '_' + + ctxIndex + '_sep'] = '----';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctxIndex++;
|
|
||||||
});
|
|
||||||
|
|
||||||
return ctxMenus;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class MenuItem {
|
export class MenuItem {
|
||||||
constructor(options, onDisableChange, onChangeChacked) {
|
constructor(options, onDisableChange, onChangeChecked) {
|
||||||
let menu_opts = [
|
let menu_opts = [
|
||||||
'name', 'label', 'priority', 'module', 'callback', 'data', 'enable',
|
'name', 'label', 'priority', 'module', 'callback', 'data', 'enable',
|
||||||
'category', 'target', 'url', 'node',
|
'category', 'target', 'url', 'node',
|
||||||
|
@ -158,7 +127,9 @@ export class MenuItem {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.onDisableChange = onDisableChange;
|
this.onDisableChange = onDisableChange;
|
||||||
this.changeChecked = onChangeChacked;
|
this.changeChecked = onChangeChecked;
|
||||||
|
this._isDisabled = true;
|
||||||
|
this.checkAndSetDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(options) {
|
static create(options) {
|
||||||
|
@ -170,6 +141,10 @@ export class MenuItem {
|
||||||
this.changeChecked?.(this);
|
this.changeChecked?.(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMenuItems() {
|
||||||
|
return this.menu_items;
|
||||||
|
}
|
||||||
|
|
||||||
contextMenuCallback(self) {
|
contextMenuCallback(self) {
|
||||||
self.callback();
|
self.callback();
|
||||||
}
|
}
|
||||||
|
@ -184,11 +159,19 @@ export class MenuItem {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setDisabled(disabled) {
|
checkAndSetDisabled(node, item, forceDisable) {
|
||||||
this.is_disabled = disabled;
|
if(!_.isUndefined(forceDisable)) {
|
||||||
|
this._isDisabled = forceDisable;
|
||||||
|
} else {
|
||||||
|
this._isDisabled = this.disabled(node, item);
|
||||||
|
}
|
||||||
this.onDisableChange?.(this.parentMenu, this);
|
this.onDisableChange?.(this.parentMenu, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isDisabled() {
|
||||||
|
return this._isDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks this menu enable/disable state based on the selection.
|
* Checks this menu enable/disable state based on the selection.
|
||||||
*/
|
*/
|
||||||
|
@ -222,7 +205,3 @@ export class MenuItem {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getContextMenu(menu, item, node) {
|
|
||||||
return Menu.getContextMenus(menu, item, node);
|
|
||||||
}
|
|
|
@ -130,18 +130,6 @@
|
||||||
.opacity-5 {
|
.opacity-5 {
|
||||||
opacity: 0.5; }
|
opacity: 0.5; }
|
||||||
|
|
||||||
.pg-navbar {
|
|
||||||
font-size: $navbar-font-size;
|
|
||||||
background-color: $navbar-bg;
|
|
||||||
padding-left: 0rem;
|
|
||||||
padding-right: 0.5rem;
|
|
||||||
& .nav-item .nav-link{
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.pg-docker {
|
.pg-docker {
|
||||||
position:absolute;
|
position:absolute;
|
||||||
left:0px;
|
left:0px;
|
||||||
|
@ -548,7 +536,7 @@ fieldset.inline-fieldset > div {
|
||||||
.pg-panel-statistics-container,
|
.pg-panel-statistics-container,
|
||||||
.pg-panel-dependencies-container,
|
.pg-panel-dependencies-container,
|
||||||
.pg-panel-dependents-container,
|
.pg-panel-dependents-container,
|
||||||
.pg-prop-coll-container, {
|
.pg-prop-coll-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
border-radius: $card-border-radius;
|
border-radius: $card-border-radius;
|
||||||
|
|
|
@ -160,8 +160,8 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
||||||
|
|
||||||
def initiate_backup(self):
|
def initiate_backup(self):
|
||||||
self.page.retry_click(
|
self.page.retry_click(
|
||||||
(By.LINK_TEXT,
|
(By.CSS_SELECTOR,
|
||||||
NavMenuLocators.tools_menu_link_text),
|
NavMenuLocators.tools_menu_css),
|
||||||
(By.CSS_SELECTOR,
|
(By.CSS_SELECTOR,
|
||||||
NavMenuLocators.backup_obj_css))
|
NavMenuLocators.backup_obj_css))
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
||||||
|
|
||||||
def initiate_restore(self):
|
def initiate_restore(self):
|
||||||
tools_menu = self.driver.find_element(
|
tools_menu = self.driver.find_element(
|
||||||
By.LINK_TEXT, NavMenuLocators.tools_menu_link_text)
|
By.CSS_SELECTOR, NavMenuLocators.tools_menu_css)
|
||||||
tools_menu.click()
|
tools_menu.click()
|
||||||
|
|
||||||
restore_obj = self.page.find_by_css_selector(
|
restore_obj = self.page.find_by_css_selector(
|
||||||
|
|
|
@ -109,8 +109,8 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest):
|
||||||
self.server['db_password'],
|
self.server['db_password'],
|
||||||
self.database_name)
|
self.database_name)
|
||||||
self.page.retry_click(
|
self.page.retry_click(
|
||||||
(By.LINK_TEXT,
|
(By.CSS_SELECTOR,
|
||||||
NavMenuLocators.tools_menu_link_text),
|
NavMenuLocators.tools_menu_css),
|
||||||
(By.CSS_SELECTOR, NavMenuLocators.maintenance_obj_css))
|
(By.CSS_SELECTOR, NavMenuLocators.maintenance_obj_css))
|
||||||
maintenance_obj = self.wait.until(EC.visibility_of_element_located(
|
maintenance_obj = self.wait.until(EC.visibility_of_element_located(
|
||||||
(By.CSS_SELECTOR, NavMenuLocators.maintenance_obj_css)))
|
(By.CSS_SELECTOR, NavMenuLocators.maintenance_obj_css)))
|
||||||
|
|
|
@ -335,14 +335,16 @@ CREATE TABLE public.nonintpkey
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _view_data_grid(self, table_name):
|
def _view_data_grid(self, table_name):
|
||||||
self.page.driver.find_element(By.LINK_TEXT, "Object").click()
|
self.page.driver.find_element(By.CSS_SELECTOR,
|
||||||
|
NavMenuLocators.object_menu_css).click()
|
||||||
ActionChains(
|
ActionChains(
|
||||||
self.page.driver
|
self.page.driver
|
||||||
).move_to_element(
|
).move_to_element(
|
||||||
self.page.driver.find_element(
|
self.page.driver.find_element(
|
||||||
By.LINK_TEXT, NavMenuLocators.view_data_link_text)
|
By.CSS_SELECTOR, NavMenuLocators.view_data_link_css)
|
||||||
).perform()
|
).perform()
|
||||||
self.page.find_by_partial_link_text("All Rows").click()
|
|
||||||
|
self.page.find_by_css_selector("li[data-label='All Rows']").click()
|
||||||
|
|
||||||
# wait until datagrid frame is loaded.
|
# wait until datagrid frame is loaded.
|
||||||
self.page.wait_for_query_tool_loading_indicator_to_appear()
|
self.page.wait_for_query_tool_loading_indicator_to_appear()
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
import secrets
|
import secrets
|
||||||
|
import time
|
||||||
|
|
||||||
from selenium.webdriver import ActionChains
|
from selenium.webdriver import ActionChains
|
||||||
from selenium.common.exceptions import TimeoutException
|
from selenium.common.exceptions import TimeoutException
|
||||||
|
@ -17,6 +18,7 @@ from regression.feature_utils.tree_area_locators import TreeAreaLocators
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
|
from regression.feature_utils.locators import NavMenuLocators
|
||||||
|
|
||||||
|
|
||||||
class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
|
class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
|
||||||
|
@ -66,13 +68,17 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
|
||||||
function_node.click()
|
function_node.click()
|
||||||
|
|
||||||
def _debug_function(self):
|
def _debug_function(self):
|
||||||
self.page.driver.find_element(By.LINK_TEXT, "Object").click()
|
self.page.driver.find_element(By.CSS_SELECTOR,
|
||||||
|
NavMenuLocators.object_menu_css).click()
|
||||||
ActionChains(
|
ActionChains(
|
||||||
self.page.driver
|
self.page.driver
|
||||||
).move_to_element(
|
).move_to_element(
|
||||||
self.page.driver.find_element(By.LINK_TEXT, "Debugging")
|
self.page.driver.find_element(
|
||||||
|
By.CSS_SELECTOR, "div[data-label='Debugging']")
|
||||||
).perform()
|
).perform()
|
||||||
self.page.driver.find_element(By.LINK_TEXT, "Debug").click()
|
time.sleep(2)
|
||||||
|
self.page.driver.find_element(
|
||||||
|
By.CSS_SELECTOR, "li[data-label='Debug']").click()
|
||||||
|
|
||||||
# We need to check if debugger plugin is installed or not
|
# We need to check if debugger plugin is installed or not
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -72,7 +72,7 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest):
|
||||||
|
|
||||||
def _check_role_membership_control(self):
|
def _check_role_membership_control(self):
|
||||||
self.page.driver.find_element(
|
self.page.driver.find_element(
|
||||||
By.LINK_TEXT, NavMenuLocators.object_menu_link_text).click()
|
By.CSS_SELECTOR, NavMenuLocators.object_menu_css).click()
|
||||||
property_object = self.wait.until(EC.visibility_of_element_located(
|
property_object = self.wait.until(EC.visibility_of_element_located(
|
||||||
(By.CSS_SELECTOR, NavMenuLocators.properties_obj_css)))
|
(By.CSS_SELECTOR, NavMenuLocators.properties_obj_css)))
|
||||||
property_object.click()
|
property_object.click()
|
||||||
|
|
|
@ -30,23 +30,23 @@ class BrowserToolBarLocators():
|
||||||
class NavMenuLocators:
|
class NavMenuLocators:
|
||||||
"This will contains element locators of navigation menu bar"
|
"This will contains element locators of navigation menu bar"
|
||||||
|
|
||||||
file_menu_css = "#mnu_file"
|
file_menu_css = "button[data-label='File']"
|
||||||
|
|
||||||
preference_menu_item_css = "#mnu_preferences"
|
preference_menu_item_css = "li[data-label='Preferences']"
|
||||||
|
|
||||||
tools_menu_link_text = "Tools"
|
tools_menu_css = "button[data-label='Tools']"
|
||||||
|
|
||||||
view_data_link_text = "View/Edit Data"
|
view_data_link_css = "div[data-label='View/Edit Data']"
|
||||||
|
|
||||||
object_menu_link_text = "Object"
|
object_menu_css = "button[data-label='Object']"
|
||||||
|
|
||||||
properties_obj_css = "#show_obj_properties.dropdown-item:not(.disabled)"
|
properties_obj_css = "li[data-label='Properties...']"
|
||||||
|
|
||||||
backup_obj_css = "#backup_object.dropdown-item:not(.disabled)"
|
backup_obj_css = "li[data-label='Backup...']"
|
||||||
|
|
||||||
restore_obj_css = "#restore_object.dropdown-item:not(.disabled)"
|
restore_obj_css = "li[data-label='Restore...']"
|
||||||
|
|
||||||
maintenance_obj_css = "#maintenance.dropdown-item:not(.disabled)"
|
maintenance_obj_css = "li[data-label='Maintenance...']"
|
||||||
|
|
||||||
show_system_objects_pref_label_xpath = \
|
show_system_objects_pref_label_xpath = \
|
||||||
"//label[contains(text(), 'Show system objects?')]"
|
"//label[contains(text(), 'Show system objects?')]"
|
||||||
|
|
|
@ -40,8 +40,8 @@ class PgadminPage:
|
||||||
# pgAdmin related methods
|
# pgAdmin related methods
|
||||||
def login_to_app(self, user_detail):
|
def login_to_app(self, user_detail):
|
||||||
self.driver.switch_to.default_content()
|
self.driver.switch_to.default_content()
|
||||||
if not (self.check_if_element_exist_by_xpath(
|
if not (self.check_if_element_exist_by_css_selector(
|
||||||
'//a[@id="navbar-user"]', 1)):
|
'button[data-test="loggedin-username"]', 1)):
|
||||||
user_edt_box_el = self.driver.find_element(By.NAME, 'email')
|
user_edt_box_el = self.driver.find_element(By.NAME, 'email')
|
||||||
user_edt_box_el.send_keys(user_detail['login_username'])
|
user_edt_box_el.send_keys(user_detail['login_username'])
|
||||||
password_edt_box_el = self.driver.find_element(By.NAME, 'password')
|
password_edt_box_el = self.driver.find_element(By.NAME, 'password')
|
||||||
|
@ -55,7 +55,8 @@ class PgadminPage:
|
||||||
attempt = 0
|
attempt = 0
|
||||||
while attempt < 4:
|
while attempt < 4:
|
||||||
try:
|
try:
|
||||||
self.click_element(self.find_by_partial_link_text("File"))
|
self.click_element(self.find_by_css_selector(
|
||||||
|
"button[data-label='File']"))
|
||||||
break
|
break
|
||||||
except (TimeoutException, NoSuchWindowException):
|
except (TimeoutException, NoSuchWindowException):
|
||||||
self.driver.refresh()
|
self.driver.refresh()
|
||||||
|
@ -66,7 +67,8 @@ class PgadminPage:
|
||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
attempt = attempt + 1
|
attempt = attempt + 1
|
||||||
|
|
||||||
self.find_by_partial_link_text("Reset Layout").click()
|
self.click_element(self.find_by_css_selector(
|
||||||
|
"li[data-label='Reset Layout']"))
|
||||||
self.click_modal('OK')
|
self.click_modal('OK')
|
||||||
self.wait_for_reloading_indicator_to_disappear()
|
self.wait_for_reloading_indicator_to_disappear()
|
||||||
|
|
||||||
|
@ -136,26 +138,26 @@ class PgadminPage:
|
||||||
(By.XPATH, server_tree_xpath)))
|
(By.XPATH, server_tree_xpath)))
|
||||||
|
|
||||||
def open_query_tool(self):
|
def open_query_tool(self):
|
||||||
self.driver.find_element(By.LINK_TEXT, "Tools").click()
|
self.click_element(self.find_by_css_selector(
|
||||||
tools_menu = self.driver.find_element(By.ID, 'mnu_tools')
|
"button[data-label='Tools']"))
|
||||||
|
self.click_element(self.find_by_css_selector(
|
||||||
query_tool = tools_menu.find_element(By.ID, 'query_tool')
|
"li[data-label='Query Tool']"))
|
||||||
|
|
||||||
self.enable_menu_item(query_tool, 10)
|
|
||||||
|
|
||||||
self.find_by_partial_link_text("Query Tool").click()
|
|
||||||
|
|
||||||
self.wait_for_element_to_be_visible(
|
self.wait_for_element_to_be_visible(
|
||||||
self.driver, "//div[@id='btn-conn-status']", 5)
|
self.driver, "//div[@id='btn-conn-status']", 5)
|
||||||
|
|
||||||
def open_view_data(self, table_name):
|
def open_view_data(self, table_name):
|
||||||
self.driver.find_element(By.LINK_TEXT, "Object").click()
|
self.click_element(self.find_by_css_selector(
|
||||||
|
NavMenuLocators.object_menu_css))
|
||||||
|
|
||||||
ActionChains(
|
ActionChains(
|
||||||
self.driver
|
self.driver
|
||||||
).move_to_element(
|
).move_to_element(
|
||||||
self.driver.find_element(By.LINK_TEXT, "View/Edit Data")
|
self.driver.find_element(
|
||||||
|
By.CSS_SELECTOR, NavMenuLocators.view_data_link_css)
|
||||||
).perform()
|
).perform()
|
||||||
self.find_by_partial_link_text("All Rows").click()
|
self.click_element(self.find_by_css_selector(
|
||||||
|
"li[data-label='All Rows']"))
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
# wait until datagrid frame is loaded.
|
# wait until datagrid frame is loaded.
|
||||||
|
|
||||||
|
@ -327,10 +329,10 @@ class PgadminPage:
|
||||||
self.driver.execute_script(
|
self.driver.execute_script(
|
||||||
self.js_executor_scrollintoview_arg, server_to_remove)
|
self.js_executor_scrollintoview_arg, server_to_remove)
|
||||||
self.click_element(server_to_remove)
|
self.click_element(server_to_remove)
|
||||||
object_menu_item = self.find_by_partial_link_text("Object")
|
self.click_element(self.find_by_css_selector(
|
||||||
self.click_element(object_menu_item)
|
"button[data-label='Object']"))
|
||||||
delete_menu_item = self.find_by_partial_link_text("Remove Server")
|
self.click_element(self.find_by_css_selector(
|
||||||
self.click_element(delete_menu_item)
|
"li[data-label='Remove Server']"))
|
||||||
self.driver.switch_to.default_content()
|
self.driver.switch_to.default_content()
|
||||||
self.click_modal('Yes')
|
self.click_modal('Yes')
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
@ -969,6 +971,18 @@ class PgadminPage:
|
||||||
pass
|
pass
|
||||||
return element_found
|
return element_found
|
||||||
|
|
||||||
|
def check_if_element_exist_by_css_selector(self, selector, timeout=5):
|
||||||
|
"""This function will verify if an element exist and on that basis
|
||||||
|
will return True or False. Will handle exception internally"""
|
||||||
|
element_found = False
|
||||||
|
try:
|
||||||
|
WebDriverWait(self.driver, timeout, .01).until(
|
||||||
|
EC.visibility_of_element_located((By.CSS_SELECTOR, selector)))
|
||||||
|
element_found = True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return element_found
|
||||||
|
|
||||||
def wait_for_element(self, find_method_with_args):
|
def wait_for_element(self, find_method_with_args):
|
||||||
def element_if_it_exists(driver):
|
def element_if_it_exists(driver):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -29,7 +29,7 @@ describe('layout related functions test', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
_.extend(pgBrowser,{
|
_.extend(pgBrowser,{
|
||||||
'menus': {
|
'all_menus_cache': {
|
||||||
'file': {
|
'file': {
|
||||||
'mnu_locklayout': {
|
'mnu_locklayout': {
|
||||||
'menu_items': [
|
'menu_items': [
|
||||||
|
@ -42,7 +42,7 @@ describe('layout related functions test', function() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
menu_items = pgBrowser.menus.file.mnu_locklayout.menu_items;
|
menu_items = pgBrowser.all_menus_cache.file.mnu_locklayout.menu_items;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('for menu actions', function() {
|
describe('for menu actions', function() {
|
||||||
|
|
|
@ -131,7 +131,6 @@ let webpackShimConfig = {
|
||||||
'pgadmin.browser.layout': path.join(__dirname, './pgadmin/browser/static/js/layout'),
|
'pgadmin.browser.layout': path.join(__dirname, './pgadmin/browser/static/js/layout'),
|
||||||
'pgadmin.browser.runtime': path.join(__dirname, './pgadmin/browser/static/js/runtime'),
|
'pgadmin.browser.runtime': path.join(__dirname, './pgadmin/browser/static/js/runtime'),
|
||||||
'pgadmin.browser.preferences': path.join(__dirname, './pgadmin/browser/static/js/preferences'),
|
'pgadmin.browser.preferences': path.join(__dirname, './pgadmin/browser/static/js/preferences'),
|
||||||
'pgadmin.browser.menu': path.join(__dirname, './pgadmin/browser/static/js/menu'),
|
|
||||||
'pgadmin.browser.activity': path.join(__dirname, './pgadmin/browser/static/js/activity'),
|
'pgadmin.browser.activity': path.join(__dirname, './pgadmin/browser/static/js/activity'),
|
||||||
'pgadmin.browser.messages': '/browser/js/messages',
|
'pgadmin.browser.messages': '/browser/js/messages',
|
||||||
'pgadmin.browser.node': path.join(__dirname, './pgadmin/browser/static/js/node'),
|
'pgadmin.browser.node': path.join(__dirname, './pgadmin/browser/static/js/node'),
|
||||||
|
@ -238,7 +237,7 @@ let webpackShimConfig = {
|
||||||
pgLibs: [
|
pgLibs: [
|
||||||
'pgadmin.browser.error',
|
'pgadmin.browser.error',
|
||||||
'pgadmin.browser.collection',
|
'pgadmin.browser.collection',
|
||||||
'pgadmin.browser.events', 'pgadmin.browser.menu', 'pgadmin.browser.panel', 'pgadmin',
|
'pgadmin.browser.events', 'pgadmin.browser.panel', 'pgadmin',
|
||||||
'pgadmin.browser.frame', 'pgadmin.browser',
|
'pgadmin.browser.frame', 'pgadmin.browser',
|
||||||
'pgadmin.browser.node',
|
'pgadmin.browser.node',
|
||||||
'pgadmin.settings', 'pgadmin.preferences', 'pgadmin.sqlfoldcode',
|
'pgadmin.settings', 'pgadmin.preferences', 'pgadmin.sqlfoldcode',
|
||||||
|
|
Loading…
Reference in New Issue