- Update MUI v4 to v5

- Remove the SCSS dependency completely and use MUI for theming.
- Update - date-fns, @date-io, notistack. Remove - popper.js, sass-loader.
- Cleanup webpack config.
- Port PSQL tool to use MUI themes instead of SCSS theme.
- Theme change will reflect realtime without refreshing pgAdmin.
This commit is contained in:
Yogesh Mahajan
2024-04-09 08:21:14 +05:30
committed by GitHub
parent edec9adbfb
commit 102e0a9839
193 changed files with 3110 additions and 5313 deletions

View File

@@ -11,11 +11,11 @@ import PropTypes from 'prop-types';
import React, { useEffect, useRef } from 'react';
import DeleteSweepIcon from '@material-ui/icons/DeleteSweep';
import { Box } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import BugReportRoundedIcon from '@material-ui/icons/BugReportRounded';
import CloseSharpIcon from '@material-ui/icons/CloseSharp';
import DeleteSweepIcon from '@mui/icons-material/DeleteSweep';
import { Box } from '@mui/material';
import { makeStyles } from '@mui/styles';
import BugReportRoundedIcon from '@mui/icons-material/BugReportRounded';
import CloseSharpIcon from '@mui/icons-material/CloseSharp';
import url_for from 'sources/url_for';
import gettext from 'sources/gettext';
@@ -883,4 +883,3 @@ DebuggerArgumentComponent.propTypes = {
pgTreeInfo: PropTypes.object,
pgData: PropTypes.object,
};

View File

@@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
import { Box } from '@material-ui/core';
import { Box } from '@mui/material';
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';

View File

@@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/styles';
import { makeStyles } from '@mui/styles';
import PropTypes from 'prop-types';
import React, { useContext, useEffect } from 'react';
@@ -69,7 +69,7 @@ export default function DebuggerEditor({ getEditor, params }) {
self = this;
getEditor(editor.current);
}, [editor.current]);
return (
<CodeMirror
currEditor={(obj) => {

View File

@@ -6,7 +6,7 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/styles';
import { makeStyles } from '@mui/styles';
import React from 'react';
import { DebuggerEventsContext } from './DebuggerComponent';
import { DEBUGGER_EVENTS } from '../DebuggerConstants';

View File

@@ -13,9 +13,9 @@ import PropTypes from 'prop-types';
import React, { useCallback, useState } from 'react';
import { makeStyles } from '@material-ui/styles';
import Paper from '@material-ui/core/Paper';
import TableContainer from '@material-ui/core/TableContainer';
import { makeStyles } from '@mui/styles';
import Paper from '@mui/material/Paper';
import TableContainer from '@mui/material/TableContainer';
import gettext from 'sources/gettext';

View File

@@ -11,8 +11,8 @@ import clsx from 'clsx';
import React, { useState } from 'react';
import { makeStyles } from '@material-ui/styles';
import Paper from '@material-ui/core/Paper';
import { makeStyles } from '@mui/styles';
import Paper from '@mui/material/Paper';
import { DebuggerEventsContext } from './DebuggerComponent';
import { DEBUGGER_EVENTS } from '../DebuggerConstants';

View File

@@ -13,9 +13,9 @@ import gettext from 'sources/gettext';
import React, { useState } from 'react';
import { makeStyles } from '@material-ui/styles';
import TableContainer from '@material-ui/core/TableContainer';
import Paper from '@material-ui/core/Paper';
import { makeStyles } from '@mui/styles';
import TableContainer from '@mui/material/TableContainer';
import Paper from '@mui/material/Paper';
import { DebuggerEventsContext } from './DebuggerComponent';
import { DEBUGGER_EVENTS } from '../DebuggerConstants';
@@ -44,7 +44,7 @@ export function Stack() {
const eventBus = React.useContext(DebuggerEventsContext);
const [stackData, setStackData] = useState([]);
const [disableFrameSelection, setDisableFrameSelection] = useState(false);
React.useEffect(() => {
eventBus.registerListener(DEBUGGER_EVENTS.SET_STACK, (stackValues) => {
setStackData(stackValues);

View File

@@ -9,15 +9,16 @@
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { Box, makeStyles } from '@material-ui/core';
import FormatIndentIncreaseIcon from '@material-ui/icons/FormatIndentIncrease';
import FormatIndentDecreaseIcon from '@material-ui/icons/FormatIndentDecrease';
import PlayCircleFilledWhiteIcon from '@material-ui/icons/PlayCircleFilledWhite';
import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
import NotInterestedIcon from '@material-ui/icons/NotInterested';
import StopIcon from '@material-ui/icons/Stop';
import HelpIcon from '@material-ui/icons/HelpRounded';
import RotateLeftRoundedIcon from '@material-ui/icons/RotateLeftRounded';
import { Box } from '@mui/material';
import { makeStyles } from '@mui/styles';
import FormatIndentIncreaseIcon from '@mui/icons-material/FormatIndentIncrease';
import FormatIndentDecreaseIcon from '@mui/icons-material/FormatIndentDecrease';
import PlayCircleFilledWhiteIcon from '@mui/icons-material/PlayCircleFilledWhite';
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
import NotInterestedIcon from '@mui/icons-material/NotInterested';
import StopIcon from '@mui/icons-material/Stop';
import HelpIcon from '@mui/icons-material/HelpRounded';
import RotateLeftRoundedIcon from '@mui/icons-material/RotateLeftRounded';
import gettext from 'sources/gettext';
import { shortcut_key } from 'sources/keyboard_shortcuts';

View File

@@ -1,22 +0,0 @@
/* To make font same as Query tool in messages tab */
.messages {
white-space: pre-wrap;
font-family: $font-family-editor;
padding-top: 5px;
padding-left: 10px;
overflow: auto;
height: 100%;
font-size: 0.925em;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.debugger-args {
display: block;
width: 100%;
height: 100%;
overflow: auto;
}

View File

@@ -11,7 +11,8 @@ import React, { useMemo } from 'react';
import gettext from 'sources/gettext';
import PropTypes from 'prop-types';
import { DefaultButton, PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
import { Box, makeStyles, Tooltip, CircularProgress } from '@material-ui/core';
import { Box, Tooltip, CircularProgress } from '@mui/material';
import { makeStyles } from '@mui/styles';
import { ConnectedIcon, DisonnectedIcon } from '../../../../../../static/js/components/ExternalIcon';
export const STATUS = {

View File

@@ -26,7 +26,8 @@ import ERDDialogs from '../dialogs';
import ConfirmSaveContent from '../../../../../../static/js/Dialogs/ConfirmSaveContent';
import Loader from '../../../../../../static/js/components/Loader';
import { MainToolBar } from './MainToolBar';
import { Box, withStyles } from '@material-ui/core';
import { Box } from '@mui/material';
import { withStyles } from '@mui/styles';
import EventBus from '../../../../../../static/js/helpers/EventBus';
import { ERD_EVENTS } from '../ERDConstants';
import getApiInstance, { callFetch, parseApiError } from '../../../../../../static/js/api_instance';

View File

@@ -11,9 +11,10 @@ import React, { useEffect, useState, useMemo } from 'react';
import gettext from 'sources/gettext';
import PropTypes from 'prop-types';
import CustomPropTypes from 'sources/custom_prop_types';
import { Box, makeStyles, Popper } from '@material-ui/core';
import { Box, Popper } from '@mui/material';
import { makeStyles } from '@mui/styles';
import { DefaultButton } from '../../../../../../static/js/components/Buttons';
import CheckIcon from '@material-ui/icons/Check';
import CheckIcon from '@mui/icons-material/Check';
const useStyles = makeStyles((theme)=>({

View File

@@ -7,27 +7,27 @@
//
//////////////////////////////////////////////////////////////
import React, {useCallback, useEffect, useState} from 'react';
import { makeStyles } from '@material-ui/styles';
import { Box, useTheme } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import { Box, useTheme } from '@mui/material';
import { PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
import FolderRoundedIcon from '@material-ui/icons/FolderRounded';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import SaveRoundedIcon from '@material-ui/icons/SaveRounded';
import HelpIcon from '@material-ui/icons/HelpRounded';
import ZoomInIcon from '@material-ui/icons/ZoomIn';
import ZoomOutIcon from '@material-ui/icons/ZoomOut';
import ZoomOutMapIcon from '@material-ui/icons/ZoomOutMap';
import AddBoxIcon from '@material-ui/icons/AddBox';
import EditRoundedIcon from '@material-ui/icons/EditRounded';
import FileCopyRoundedIcon from '@material-ui/icons/FileCopyRounded';
import DeleteIcon from '@material-ui/icons/Delete';
import NoteRoundedIcon from '@material-ui/icons/NoteRounded';
import VisibilityRoundedIcon from '@material-ui/icons/VisibilityRounded';
import VisibilityOffRoundedIcon from '@material-ui/icons/VisibilityOffRounded';
import ImageRoundedIcon from '@material-ui/icons/ImageRounded';
import FormatColorFillRoundedIcon from '@material-ui/icons/FormatColorFillRounded';
import FormatColorTextRoundedIcon from '@material-ui/icons/FormatColorTextRounded';
import AccountTreeOutlinedIcon from '@material-ui/icons/AccountTreeOutlined';
import FolderRoundedIcon from '@mui/icons-material/FolderRounded';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import SaveRoundedIcon from '@mui/icons-material/SaveRounded';
import HelpIcon from '@mui/icons-material/HelpRounded';
import ZoomInIcon from '@mui/icons-material/ZoomIn';
import ZoomOutIcon from '@mui/icons-material/ZoomOut';
import ZoomOutMapIcon from '@mui/icons-material/ZoomOutMap';
import AddBoxIcon from '@mui/icons-material/AddBox';
import EditRoundedIcon from '@mui/icons-material/EditRounded';
import FileCopyRoundedIcon from '@mui/icons-material/FileCopyRounded';
import DeleteIcon from '@mui/icons-material/Delete';
import NoteRoundedIcon from '@mui/icons-material/NoteRounded';
import VisibilityRoundedIcon from '@mui/icons-material/VisibilityRounded';
import VisibilityOffRoundedIcon from '@mui/icons-material/VisibilityOffRounded';
import ImageRoundedIcon from '@mui/icons-material/ImageRounded';
import FormatColorFillRoundedIcon from '@mui/icons-material/FormatColorFillRounded';
import FormatColorTextRoundedIcon from '@mui/icons-material/FormatColorTextRounded';
import AccountTreeOutlinedIcon from '@mui/icons-material/AccountTreeOutlined';
import { PgMenu, PgMenuItem, usePgMenuGroup } from '../../../../../../static/js/components/Menu';
import gettext from 'sources/gettext';

View File

@@ -19,7 +19,7 @@ import {
import {Point} from '@projectstorm/geometry';
import _ from 'lodash';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import clsx from 'clsx';
import { ERDCanvasSettings } from '../components/ERDTool';

View File

@@ -20,12 +20,12 @@ import UniqueKeyIcon from 'top/browser/server_groups/servers/databases/schemas/t
import PropTypes from 'prop-types';
import gettext from 'sources/gettext';
import { PgIconButton } from '../../../../../../static/js/components/Buttons';
import NoteRoundedIcon from '@material-ui/icons/NoteRounded';
import VisibilityRoundedIcon from '@material-ui/icons/VisibilityRounded';
import VisibilityOffRoundedIcon from '@material-ui/icons/VisibilityOffRounded';
import { withStyles } from '@material-ui/styles';
import NoteRoundedIcon from '@mui/icons-material/NoteRounded';
import VisibilityRoundedIcon from '@mui/icons-material/VisibilityRounded';
import VisibilityOffRoundedIcon from '@mui/icons-material/VisibilityOffRounded';
import { withStyles } from '@mui/styles';
import clsx from 'clsx';
import { Box } from '@material-ui/core';
import { Box } from '@mui/material';
const TYPE = 'table';

View File

@@ -11,8 +11,8 @@ import gettext from 'sources/gettext';
import _ from 'lodash';
import url_for from 'sources/url_for';
import React from 'react';
import { Box } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import { Box } from '@mui/material';
import { makeStyles } from '@mui/styles';
import Wizard from '../../../../static/js/helpers/wizard/Wizard';
import WizardStep from '../../../../static/js/helpers/wizard/WizardStep';
import PgTable from 'sources/components/PgTable';
@@ -363,5 +363,3 @@ GrantWizard.propTypes = {
nodeData: PropTypes.object,
onClose: PropTypes.func
};

View File

@@ -1,130 +0,0 @@
/** Grant Wizard CSS **/
/**
CSS to make db object type table
fixed so that tbody content may
scroll
*/
.db_objects_container {
height: 100%;
padding: 0;
}
.db_objects_grid {
height: calc(100% - 15px);
}
.db_objects_filter {
& .input-group-text {
background: $input-bg;
}
& .form-control {
border-left: none;
}
}
.object_type_table {
height: calc(100% - 15px);
display: block;
padding: 0;
overflow-y: hidden !important;
}
.object_type_table thead {
display: table;
width: 100%;
}
.object_type_table thead tr {
position: relative;
display: block;
width: 100%;
border-bottom: $panel-border;
}
.object_type_table tbody {
display: block;
overflow: auto;
width: 100%;
/* 100% minus thead height */
height: calc(100% - 40px);
}
.object_type_table tbody tr {
max-width: 100%;
width: 100%;
}
.object_type_table tbody tr td {
background-position: 4px 4px;
border-radius: 0;
}
.object_type_table tbody tr td:nth-child(1),
.object_type_table thead tr th:nth-child(1) {
width: 28px;
min-width: 28px;
}
.object_type_table tbody tr td:nth-child(2) {
width: 161px;
min-width: 161px;
max-width: 161px;
}
.object_type_table thead tr th:nth-child(2) {
width: 161px;
min-width: 161px;
max-width: 161px;
}
.object_type_table tbody tr td:nth-child(3) {
width: 109px;
min-width: 109px;
max-width: 109px;
}
.object_type_table thead tr th:nth-child(3) {
width: 109px;
min-width: 109px;
max-width: 109px;
}
.object_type_table thead tr th:nth-child(4) {
width: 100%;
}
.object_type_table tbody tr td:nth-child(4) {
width: 100%;
max-width: 100%;
}
/** Custom styling for Codemirror field **/
.wizard-right-panel_content {
border: 1px $color-gray-light;
padding: 0.5rem 0rem;
height: calc(100% - #{$footer-height-calc});
}
.grant_wizard_container {
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
}
#grantWizardDlg {
padding-top: 0em;
}
.grant-wizard-objcol {
min-width: 8em !important;
width: 8em !important;
}
.grant-wizard-panel-content {
padding-top: 0.9em !important;
}

View File

@@ -11,8 +11,8 @@ import gettext from 'sources/gettext';
import _ from 'lodash';
import url_for from 'sources/url_for';
import React from 'react';
import { Box, Paper} from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import { Box, Paper} from '@mui/material';
import { makeStyles } from '@mui/styles';
import Wizard from '../../../../static/js/helpers/wizard/Wizard';
import WizardStep from '../../../../static/js/helpers/wizard/WizardStep';
import { FormFooterMessage, MESSAGE_TYPE, FormNote } from '../../../../static/js/components/FormComponents';

View File

@@ -101,18 +101,21 @@ def panel(trans_id):
data = _get_database_role(params['sid'], params['did'])
params = {
'sid': params['sid'],
'db': underscore_escape(data['db_name']),
'server_type': params['server_type'],
'is_enable': config.ENABLE_PSQL,
'title': underscore_unescape(params['title']),
'theme': params['theme'],
'o_db_name': underscore_escape(data['db_name']),
'role': underscore_escape(data['role']),
'platform': _platform
}
set_env_variables(is_win=_platform == 'win32')
return render_template('editor_template.html',
sid=params['sid'],
db=underscore_escape(data['db_name']),
server_type=params['server_type'],
is_enable=config.ENABLE_PSQL,
title=underscore_unescape(params['title']),
theme=params['theme'],
o_db_name=underscore_escape(data['db_name']),
role=underscore_escape(data['role']),
platform=_platform
)
return render_template("psql/index.html",
params=json.dumps(params))
def set_env_variables(is_win=False):

View File

@@ -0,0 +1,202 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { getRandomInt, hasBinariesConfiguration } from 'sources/utils';
import { retrieveAncestorOfTypeServer } from 'sources/tree/tree_utils';
import { generateTitle } from 'tools/sqleditor/static/js/sqleditor_title';
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
import usePreferences,{ listenPreferenceBroadcast } from '../../../../preferences/static/js/store';
import 'pgadmin.browser.keyboard';
import pgWindow from 'sources/window';
import pgAdmin from 'sources/pgadmin';
import pgBrowser from 'pgadmin.browser';
import PsqlComponent from './components/PsqlComponent';
import { PgAdminContext } from '../../../../static/js/BrowserComponent';
import getApiInstance from '../../../../static/js/api_instance';
import gettext from 'sources/gettext';
import url_for from 'sources/url_for';
import Theme from '../../../../static/js/Theme';
import { NotifierProvider } from '../../../../static/js/helpers/Notifier';
import ModalProvider from '../../../../static/js/helpers/ModalProvider';
import * as csrfToken from 'sources/csrf';
import React from 'react';
import ReactDOM from 'react-dom';
export default class Psql {
static instance;
static getInstance(...args) {
if (!Psql.instance) {
Psql.instance = new Psql(...args);
}
return Psql.instance;
}
constructor(pgAdmin, pgBrowser) {
this.pgAdmin = pgAdmin;
this.pgBrowser = pgBrowser;
this.api = getApiInstance();
}
/* Enable/disable PSQL tool menu in tools based
* on node selected. if selected node is present
* in unsupported_nodes, menu will be disabled
* otherwise enabled.
*/
psqlToolEnabled(obj) {
let isEnabled = (() => {
if (!_.isUndefined(obj) && !_.isNull(obj) && pgAdmin['enable_psql']) {
if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) {
if (obj._type == 'database' && obj.allowConn) {
return true;
} else if (obj._type != 'database') {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
})();
return isEnabled;
}
init() {
if (this.initialized)
return;
this.initialized = true;
csrfToken.setPGCSRFToken(pgAdmin.csrf_token_header, pgAdmin.csrf_token);
// Define the nodes on which the menus to be appear
let menus = [{
name: 'psql',
module: this,
applies: ['tools'],
callback: 'openPsqlTool',
enable: this.psqlToolEnabled,
priority: 1,
label: gettext('PSQL Tool'),
data:{
applies: 'tools',
data_disabled: gettext('Please select a database from the object explorer to access Pql Tool.'),
},
}];
this.enable_psql_tool = pgAdmin['enable_psql'];
if(pgAdmin['enable_psql']) {
pgBrowser.add_menus(menus);
}
}
openPsqlTool(data, treeIdentifier) {
const serverInformation = retrieveAncestorOfTypeServer(pgBrowser, treeIdentifier, gettext('PSQL Error'));
if (!hasBinariesConfiguration(pgBrowser, serverInformation)) {
return;
}
const node = pgBrowser.tree.findNodeByDomElement(treeIdentifier);
if (node === undefined || !node.getData()) {
pgAdmin.Browser.notifier.alert(
gettext('PSQL Error'),
gettext('No object selected.')
);
return;
}
const parentData = pgBrowser.tree.getTreeNodeHierarchy(treeIdentifier);
if(_.isUndefined(parentData.server)) {
pgAdmin.Browser.notifier.alert(
gettext('PSQL Error'),
gettext('Please select a server/database object.')
);
return;
}
const transId = getRandomInt(1, 9999999);
let panelTitle = '';
// Set psql tab title as per prefrences setting.
let title_data = {
'database': parentData.database ? _.unescape(parentData.database.label) : 'postgres' ,
'username': parentData.server.user.name,
'server': parentData.server.label,
'type': 'psql_tool',
};
let tab_title_placeholder = usePreferences.getState().getPreferencesForModule('browser').psql_tab_title_placeholder;
panelTitle = generateTitle(tab_title_placeholder, title_data);
const [panelUrl, db_label] = this.getPanelUrls(transId, parentData);
const escapedTitle = _.escape(panelTitle);
const open_new_tab = usePreferences.getState().getPreferencesForModule('browser').new_browser_tab_open;
pgAdmin.Browser.Events.trigger(
'pgadmin:tool:show',
`${BROWSER_PANELS.PSQL_TOOL}_${transId}`,
panelUrl,
{title: escapedTitle, db: db_label},
{title: panelTitle, icon: 'fas fa-terminal psql-tab-style', manualClose: false, renamable: true},
Boolean(open_new_tab?.includes('psql_tool'))
);
return true;
}
getPanelUrls(transId, pData) {
let openUrl = url_for('psql.panel', {
trans_id: transId,
});
const misc_preferences = usePreferences.getState().getPreferencesForModule('misc');
let theme = misc_preferences.theme;
openUrl += `?sgid=${pData.server_group._id}`
+`&sid=${pData.server._id}`
+`&did=${pData.database._id}`
+`&server_type=${pData.server.server_type}`
+ `&theme=${theme}`;
if(pData.database?._id) {
openUrl += `&db=${encodeURIComponent(pData.database._label)}`;
} else {
openUrl += `&db=${''}`;
}
return [openUrl, pData.database._label];
}
async loadComponent(container, params) {
pgAdmin.Browser.keyboardNavigation.init();
await listenPreferenceBroadcast();
ReactDOM.render(
<Theme>
<PgAdminContext.Provider value={pgAdmin}>
<ModalProvider>
<NotifierProvider pgAdmin={pgAdmin} pgWindow={pgWindow} />
<PsqlComponent params={params} pgAdmin={pgAdmin} />
</ModalProvider>
</PgAdminContext.Provider>
</Theme>,
container
);
}
}

View File

@@ -0,0 +1,203 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React, { useEffect } from 'react';
import { Box, useTheme } from '@mui/material';
import url_for from 'sources/url_for';
import PropTypes from 'prop-types';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { WebLinksAddon } from 'xterm-addon-web-links';
import { SearchAddon } from 'xterm-addon-search';
import { io } from 'socketio';
import { copyToClipboard } from '../../../../../static/js/clipboard';
import 'pgadmin.browser.keyboard';
import gettext from 'sources/gettext';
function psql_socket_io(socket, is_enable, sid, db, server_type, fitAddon, term, role){
// Listen all the socket events emit from server.
let init_psql = true;
socket.on('pty-output', function(data){
if(data.error) {
term.write('\r\n');
}
term.write(data.result);
if(data.error) {
term.write('\r\n');
}
if (init_psql && data && role) {
// setting role if available
socket.emit('socket_set_role',{'role': _.unescape(role)});
init_psql = false;
}
});
// Connect socket
socket.on('connect', () => {
if(is_enable){
socket.emit('start_process', {'sid': sid, 'db': db, 'stype': server_type });
}
fitAddon.fit();
socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
});
socket.on('conn_error', (response) => {
term.write(response.error);
fitAddon.fit();
socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
});
socket.on('conn_not_allow', () => {
term.write('PSQL connection not allowed');
fitAddon.fit();
socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
});
socket.on('disconnect-psql', () => {
socket.emit('server-disconnect', {'sid': sid});
term.write('\r\nServer disconnected, Connection terminated, To create new connection please open another psql tool.');
});
}
function psql_terminal_io(term, socket, platform, pgAdmin) {
// Listen key press event from terminal and emit socket event.
term.attachCustomKeyEventHandler(e => {
e.stopPropagation();
if(e.type=='keydown' && (e.metaKey || e.ctrlKey) && (e.key == 'c' || e.key == 'C')) {
let selected_text = term.getSelection();
navigator.permissions.query({ name: 'clipboard-write' }).then(function(result) {
if(result.state === 'granted' || result.state === 'prompt') {
copyToClipboard(selected_text);
} else {
pgAdmin.Browser.notifier.alert(gettext('Clipboard write permission required'), gettext('To copy data from PSQL terminal, Clipboard write permission required.'));
}
});
} else {
self.pgAdmin.Browser.keyboardNavigation.triggerIframeEventsBroadcast(e,true);
}
return !(e.ctrlKey && platform == 'win32');
});
term.textarea.addEventListener('paste', function() {
navigator.permissions.query({ name: 'clipboard-read' }).then(function(result) {
if(result.state === 'granted' || result.state === 'prompt') {
navigator.clipboard.readText().then( clipText => {
let selected_text = clipText;
if (selected_text.length > 0) {
socket.emit('socket_input', {'input': selected_text, 'key_name': 'paste'});
}
});
} else{
pgAdmin.Browser.notifier.alert(gettext('Clipboard read permission required'), gettext('To paste data on the PSQL terminal, Clipboard read permission required.'));
}
});
});
term.onKey(function (ev) {
socket.emit('socket_input', {'input': ev.key, 'key_name': ev.domEvent.code});
});
}
function psql_Addon(term) {
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
const webLinksAddon = new WebLinksAddon();
term.loadAddon(webLinksAddon);
const searchAddon = new SearchAddon();
term.loadAddon(searchAddon);
fitAddon.fit();
term.resize(15, 50);
fitAddon.fit();
return fitAddon;
}
function psql_socket() {
return io('/pty', {
path: `${url_for('pgadmin.root')}/socket.io`,
pingTimeout: 120000,
pingInterval: 25000
});
}
export default function PsqlComponent({ params, pgAdmin }) {
const theme = useTheme();
const termRef = React.useRef(null);
const containerRef = React.useRef(null);
const initializePsqlTool = (params)=>{
const term = new Terminal({
cursorBlink: true,
scrollback: 5000,
});
/* Addon for fitAddon, webLinkAddon, SearchAddon */
const fitAddon = psql_Addon(term);
term.open(containerRef.current);
/* Socket */
const socket = psql_socket();
psql_socket_io(socket, params.is_enable, params.sid, params.db, params.server_type, fitAddon, term, params.role);
psql_terminal_io(term, socket, params.platform, pgAdmin);
/* Set terminal size */
setTimeout(function(){
socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
}, 1000);
return [term, socket];
};
useEffect(()=>{
const [term, socket] = initializePsqlTool(params);
termRef.current = term;
termRef.current?.setOption('theme', {
background: '#ff0000'});
return () => {
term.dispose();
socket.disconnect();
};
}, []);
useEffect(()=>{
let psqlTheme = {
background: theme.palette.background.default,
foreground: theme.palette.text.primary,
cursor: theme.palette.text.primary,
cursorAccent: theme.palette.text.primary,
selection: theme.palette.primary.main
};
termRef.current?.setOption('theme', psqlTheme );
},[theme]);
return (
<Box width="100%" height="100%" display="flex" flexDirection="column" flexGrow="1" tabIndex="0" ref={containerRef}>
</Box>
);
}
PsqlComponent.propTypes = {
params:PropTypes.shape({
is_enable: PropTypes.Boolean,
sid: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
db: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
server_type: PropTypes.string,
role: PropTypes.string,
platform: PropTypes.string
}),
pgAdmin: PropTypes.object.isRequired,
};

View File

@@ -7,16 +7,17 @@
//
//////////////////////////////////////////////////////////////
import gettext from 'sources/gettext';
import url_for from 'sources/url_for';
import _ from 'lodash';
import pgAdmin from 'sources/pgadmin';
import pgBrowser from 'top/browser/static/js/browser';
import * as csrfToken from 'sources/csrf';
import {initialize} from './psql_module';
import Psql from './PsqlModule';
let pgBrowserOut = initialize(gettext, url_for, _, pgAdmin, csrfToken, pgBrowser);
if(!pgAdmin.Tools) {
pgAdmin.Tools = {};
}
pgAdmin.Tools.Psql = Psql.getInstance(pgAdmin, pgBrowser);
module.exports = {
pgBrowser: pgBrowserOut,
Psql: Psql
};

View File

@@ -1,342 +0,0 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { WebLinksAddon } from 'xterm-addon-web-links';
import { SearchAddon } from 'xterm-addon-search';
import { io } from 'socketio';
import {getRandomInt, hasBinariesConfiguration} from 'sources/utils';
import {retrieveAncestorOfTypeServer} from 'sources/tree/tree_utils';
import pgWindow from 'sources/window';
import { copyToClipboard } from '../../../../static/js/clipboard';
import {generateTitle, refresh_db_node} from 'tools/sqleditor/static/js/sqleditor_title';
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
import usePreferences,{ listenPreferenceBroadcast } from '../../../../preferences/static/js/store';
import 'pgadmin.browser.keyboard';
export function setPanelTitle(psqlToolPanel, panelTitle) {
psqlToolPanel.title('<span title="'+_.escape(panelTitle)+'">'+_.escape(panelTitle)+'</span>');
}
export function initialize(gettext, url_for, _, pgAdmin, csrfToken, Browser) {
let pgBrowser = Browser;
let terminal = Terminal;
let parentData = null;
/* Return back, this has been called more than once */
if (pgBrowser.psql)
return pgBrowser.psql;
// Create an Object Restore of pgBrowser class
pgBrowser.psql = {
init: function() {
this.initialized = true;
csrfToken.setPGCSRFToken(pgAdmin.csrf_token_header, pgAdmin.csrf_token);
// Define the nodes on which the menus to be appear
let menus = [{
name: 'psql',
module: this,
applies: ['tools'],
callback: 'psql_tool',
priority: 1,
label: gettext('PSQL Tool'),
enable: this.psqlToolEnabled,
}];
this.enable_psql_tool = pgAdmin['enable_psql'];
if(pgAdmin['enable_psql']) {
pgBrowser.add_menus(menus);
}
return this;
},
/* Enable/disable PSQL tool menu in tools based
* on node selected. if selected node is present
* in unsupported_nodes, menu will be disabled
* otherwise enabled.
*/
psqlToolEnabled: function(obj) {
let isEnabled = (() => {
if (!_.isUndefined(obj) && !_.isNull(obj) && pgAdmin['enable_psql']) {
if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) {
if (obj._type == 'database' && obj.allowConn) {
return true;
} else if (obj._type != 'database') {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
})();
return isEnabled;
},
psql_tool: function(data, treeIdentifier) {
const serverInformation = retrieveAncestorOfTypeServer(pgBrowser, treeIdentifier, gettext('PSQL Error'));
if (!hasBinariesConfiguration(pgBrowser, serverInformation)) {
return;
}
const node = pgBrowser.tree.findNodeByDomElement(treeIdentifier);
if (node === undefined || !node.getData()) {
pgAdmin.Browser.notifier.alert(
gettext('PSQL Error'),
gettext('No object selected.')
);
return;
}
parentData = pgBrowser.tree.getTreeNodeHierarchy(treeIdentifier);
if(_.isUndefined(parentData.server)) {
pgAdmin.Browser.notifier.alert(
gettext('PSQL Error'),
gettext('Please select a server/database object.')
);
return;
}
const transId = getRandomInt(1, 9999999);
let panelTitle = '';
// Set psql tab title as per prefrences setting.
let title_data = {
'database': parentData.database ? _.unescape(parentData.database.label) : 'postgres' ,
'username': parentData.server.user.name,
'server': parentData.server.label,
'type': 'psql_tool',
};
let tab_title_placeholder = usePreferences.getState().getPreferencesForModule('browser').psql_tab_title_placeholder;
panelTitle = generateTitle(tab_title_placeholder, title_data);
const [panelUrl, db_label] = this.getPanelUrls(transId, parentData);
const escapedTitle = _.escape(panelTitle);
const open_new_tab = usePreferences.getState().getPreferencesForModule('browser').new_browser_tab_open;
pgAdmin.Browser.Events.trigger(
'pgadmin:tool:show',
`${BROWSER_PANELS.PSQL_TOOL}_${transId}`,
panelUrl,
{title: escapedTitle, db: db_label},
{title: panelTitle, icon: 'fas fa-terminal psql-tab-style', manualClose: false, renamable: true},
Boolean(open_new_tab?.includes('psql_tool'))
);
return true;
},
getPanelUrls: function(transId, pData) {
let openUrl = url_for('psql.panel', {
trans_id: transId,
});
const misc_preferences = usePreferences.getState().getPreferencesForModule('misc');
let theme = misc_preferences.theme;
openUrl += `?sgid=${pData.server_group._id}`
+`&sid=${pData.server._id}`
+`&did=${pData.database._id}`
+`&server_type=${pData.server.server_type}`
+ `&theme=${theme}`;
if(pData.database?._id) {
openUrl += `&db=${encodeURIComponent(pData.database._label)}`;
} else {
openUrl += `&db=${''}`;
}
return [openUrl, pData.database._label];
},
psql_terminal: function() {
// theme colors
return new terminal({
cursorBlink: true,
scrollback: 5000,
});
},
psql_Addon: function(term) {
const fitAddon = this.psql_fit_screen();
term.loadAddon(fitAddon);
const webLinksAddon = this.psql_web_link();
term.loadAddon(webLinksAddon);
const searchAddon = this.psql_search();
term.loadAddon(searchAddon);
fitAddon.fit();
term.resize(15, 50);
fitAddon.fit();
return fitAddon;
},
psql_fit_screen: function() {
return new FitAddon();
},
psql_web_link: function() {
return new WebLinksAddon();
},
psql_search: function() {
return new SearchAddon();
},
psql_socket: function() {
return io('/pty', {
path: `${url_for('pgadmin.root')}/socket.io`,
pingTimeout: 120000,
pingInterval: 25000
});
},
set_theme: function(term) {
let theme = {
background: getComputedStyle(document.documentElement).getPropertyValue('--psql-background'),
foreground: getComputedStyle(document.documentElement).getPropertyValue('--psql-foreground'),
cursor: getComputedStyle(document.documentElement).getPropertyValue('--psql-cursor'),
cursorAccent: getComputedStyle(document.documentElement).getPropertyValue('--psql-cursorAccent'),
selection: getComputedStyle(document.documentElement).getPropertyValue('--psql-selection'),
};
term.setOption('theme', theme);
},
psql_socket_io: function(socket, is_enable, sid, db, server_type, fitAddon, term, role) {
// Listen all the socket events emit from server.
let init_psql = true;
socket.on('pty-output', function(data){
if(data.error) {
term.write('\r\n');
}
term.write(data.result);
if(data.error) {
term.write('\r\n');
}
if (init_psql && data && role !== 'None') {
// setting role if available
socket.emit('socket_set_role',{'role': _.unescape(role)});
init_psql = false;
}
});
// Connect socket
socket.on('connect', () => {
if(is_enable == 'True'){
socket.emit('start_process', {'sid': sid, 'db': db, 'stype': server_type });
}
fitAddon.fit();
socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
});
socket.on('conn_error', (response) => {
term.write(response.error);
fitAddon.fit();
socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
});
socket.on('conn_not_allow', () => {
term.write('PSQL connection not allowed');
fitAddon.fit();
socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
});
socket.on('disconnect-psql', () => {
socket.emit('server-disconnect', {'sid': sid});
term.write('\r\nServer disconnected, Connection terminated, To create new connection please open another psql tool.');
});
},
psql_terminal_io: function(term, socket, platform) {
// Listen key press event from terminal and emit socket event.
term.attachCustomKeyEventHandler(e => {
e.stopPropagation();
if(e.type=='keydown' && (e.metaKey || e.ctrlKey) && (e.key == 'c' || e.key == 'C')) {
let selected_text = term.getSelection();
navigator.permissions.query({ name: 'clipboard-write' }).then(function(result) {
if(result.state === 'granted' || result.state === 'prompt') {
copyToClipboard(selected_text);
} else {
pgAdmin.Browser.notifier.alert(gettext('Clipboard write permission required'), gettext('To copy data from PSQL terminal, Clipboard write permission required.'));
}
});
} else {
self.pgAdmin.Browser.keyboardNavigation.triggerIframeEventsBroadcast(e,true);
}
return !(e.ctrlKey && platform == 'win32');
});
term.textarea.addEventListener('paste', function() {
navigator.permissions.query({ name: 'clipboard-read' }).then(function(result) {
if(result.state === 'granted' || result.state === 'prompt') {
navigator.clipboard.readText().then( clipText => {
let selected_text = clipText;
if (selected_text.length > 0) {
socket.emit('socket_input', {'input': selected_text, 'key_name': 'paste'});
}
});
} else{
pgAdmin.Browser.notifier.alert(gettext('Clipboard read permission required'), gettext('To paste data on the PSQL terminal, Clipboard read permission required.'));
}
});
});
term.onKey(function (ev) {
socket.emit('socket_input', {'input': ev.key, 'key_name': ev.domEvent.code});
});
},
check_db_name_change: function(db_name, o_db_name) {
if (db_name != o_db_name) {
let selected_item = pgWindow.pgAdmin.Browser.tree.selected(),
tree_data = pgWindow.pgAdmin.Browser.tree.translateTreeNodeIdFromReactTree(selected_item),
database_data = pgWindow.pgAdmin.Browser.tree.findNode(tree_data[3]),
dbNode = database_data.domNode;
let message = `Current database has been moved or renamed to ${o_db_name}. Click on the OK button to refresh the database name, and reopen the psql again.`;
refresh_db_node(message, dbNode);
}
},
psql_mount: async function(params){
self.pgAdmin.Browser.keyboardNavigation.init();
await listenPreferenceBroadcast();
const term = self.pgAdmin.Browser.psql.psql_terminal();
/* Addon for fitAddon, webLinkAddon, SearchAddon */
const fitAddon = self.pgAdmin.Browser.psql.psql_Addon(term);
/* Update the theme for terminal as per pgAdmin 4 theme. */
self.pgAdmin.Browser.psql.set_theme(term);
/* Open the terminal */
term.open(document.getElementById('psql-terminal'));
/* Socket */
const socket = self.pgAdmin.Browser.psql.psql_socket();
self.pgAdmin.Browser.psql.psql_socket_io(socket, params.is_enable, params.sid, params.db, params.server_type, fitAddon, term, params.role);
self.pgAdmin.Browser.psql.psql_terminal_io(term, socket, params.platform);
self.pgAdmin.Browser.psql.check_db_name_change(params.db, params.o_db_name);
/* Set terminal size */
setTimeout(function(){
socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
}, 1000);
/* Resize the terminal */
function fitToscreen(){
fitAddon.fit();
socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
}
function debounce(func, wait_ms) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait_ms);
};
}
window.onresize = debounce(fitToscreen, 25);
}
};
return pgBrowser.psql;
}

View File

@@ -1,34 +0,0 @@
{% extends "base.html" %}
{% block title %}{{title}}{% endblock %}
{% block css_link %}
<link type="text/css" rel="stylesheet" href="{{ url_for('browser.browser_css')}}"/>
{% endblock %}
{% block body %}
<style>
body {padding: 0px;}
{% if is_desktop_mode and is_linux %}
.sql-editor-busy-icon.fa-pulse{-webkit-animation: none;}
{% endif %}
</style>
<div style="width: 100%; height: 100%;" id="psql-terminal" class="psql_terminal"></div>
{% endblock %}
{% block init_script %}
require(
['sources/generated/psql_tool'],
function(pgBrowser) {
<!-- Call the PSQL mount-->
self.pgAdmin.Browser.psql.psql_mount({
"is_enable": '{{is_enable}}',
"sid": '{{sid}}',
"db":'{{db|safe}}',
"server_type": '{{server_type}}',
"role": '{{role|safe}}',
"platform": '{{platform}}',
"o_db_name": '{{o_db_name|safe}}'
})
});
{% endblock %}

View File

@@ -0,0 +1,56 @@
{% extends "base.html" %}
{% block title %}{{title}}{% endblock %}
{% block css_link %}
<style>
#psql-container {
display: flex;
flex-direction: column;
height: 100%;
}
#psql-container:not(:empty) + .pg-sp-container {
display: none;
}
{% if is_desktop_mode and is_linux %}
.sql-editor-busy-icon.fa-pulse{-webkit-animation: none;}
{% endif %}
</style>
<link type="text/css" rel="stylesheet" href="{{ url_for('browser.browser_css')}}"/>
{% endblock %}
{% block body %}
<div id="psql-container" tabindex="0">
<div class="pg-sp-container">
<div class="pg-sp-content">
<div class="row">
<div class="col-12 pg-sp-icon"></div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block init_script %}
try {
require(
['sources/generated/browser_nodes'],
function() {
require(['sources/generated/psql_tool'], function(module) {
window.pgAdmin.Tools.Psql.loadComponent(
document.getElementById('psql-container'),{{ params|safe }});
}, function() {
console.log(arguments);
});
},
function() {
console.log(arguments);
});
} catch (err) {
console.log(err);
}
{% endblock %}

View File

@@ -10,8 +10,8 @@ import PropTypes from 'prop-types';
import React, { useContext, useState } from 'react';
import { Box, Grid, Typography } from '@material-ui/core';
import { makeStyles } from '@material-ui/styles';
import { Box, Grid, Typography } from '@mui/material';
import { makeStyles } from '@mui/styles';
import { InputSelect } from '../../../../../static/js/components/FormComponents';
import { SchemaDiffEventsContext } from './SchemaDiffComponent';

View File

@@ -13,11 +13,11 @@ import clsx from 'clsx';
import { SelectColumn } from 'react-data-grid';
import React, { useContext, useEffect, useLayoutEffect, useReducer, useRef, useState } from 'react';
import { Box } from '@material-ui/core';
import { makeStyles } from '@material-ui/styles';
import KeyboardArrowRightRoundedIcon from '@material-ui/icons/KeyboardArrowRightRounded';
import KeyboardArrowDownRoundedIcon from '@material-ui/icons/KeyboardArrowDownRounded';
import InfoIcon from '@material-ui/icons/InfoRounded';
import { Box } from '@mui/material';
import { makeStyles } from '@mui/styles';
import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded';
import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded';
import InfoIcon from '@mui/icons-material/InfoRounded';
import gettext from 'sources/gettext';
import url_for from 'sources/url_for';

View File

@@ -10,8 +10,8 @@ import gettext from 'sources/gettext';
import React, { useContext, useState, useEffect } from 'react';
import { Box } from '@material-ui/core';
import { makeStyles } from '@material-ui/styles';
import { Box } from '@mui/material';
import { makeStyles } from '@mui/styles';
import { InputSQL } from '../../../../../static/js/components/FormComponents';
import { SchemaDiffEventsContext } from './SchemaDiffComponent';
import { SCHEMA_DIFF_EVENT } from '../SchemaDiffConstants';
@@ -84,7 +84,7 @@ export function Results() {
<Box className={classes.label}>{gettext('Difference')}</Box>
</Box>
<Box className={classes.sqlContainer}>
<Box className={classes.sqldata}>
<Box className={classes.sqldata}>
<Box className={classes.sqlInput}>
<InputSQL
onLable={true}
@@ -135,4 +135,4 @@ export function Results() {
Results.propTypes = {
};
};

View File

@@ -13,11 +13,11 @@ import React, { useState, useRef, useContext, useEffect } from 'react';
import gettext from 'sources/gettext';
import { Box } from '@material-ui/core';
import CompareArrowsRoundedIcon from '@material-ui/icons/CompareArrowsRounded';
import FeaturedPlayListRoundedIcon from '@material-ui/icons/FeaturedPlayListRounded';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import { makeStyles } from '@material-ui/styles';
import { Box } from '@mui/material';
import CompareArrowsRoundedIcon from '@mui/icons-material/CompareArrowsRounded';
import FeaturedPlayListRoundedIcon from '@mui/icons-material/FeaturedPlayListRounded';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import { makeStyles } from '@mui/styles';
import { DefaultButton, PgButtonGroup, PgIconButton, PrimaryButton } from '../../../../../static/js/components/Buttons';
import { FilterIcon } from '../../../../../static/js/components/ExternalIcon';
@@ -169,7 +169,7 @@ export function SchemaDiffButtonComponent({ sourceData, targetData, selectedRowI
<PgButtonGroup size="small" disabled={isDisableCompare}>
<PrimaryButton startIcon={<CompareArrowsRoundedIcon />}
onClick={compareDiff}>{gettext('Compare')}</PrimaryButton>
<PgIconButton title={gettext('Compare')} icon={<KeyboardArrowDownIcon />} color={'primary'} splitButton
<PgIconButton title={gettext('Compare')} disabled={isDisableCompare} icon={<KeyboardArrowDownIcon />} color={'primary'} splitButton
name={MENUS.COMPARE} ref={compareRef} onClick={toggleMenu} ></PgIconButton>
</PgButtonGroup>
</Box>
@@ -182,7 +182,7 @@ export function SchemaDiffButtonComponent({ sourceData, targetData, selectedRowI
<PgButtonGroup size="small" disabled={isDisableCompare} style={{ paddingRight: '0.3rem' }}>
<DefaultButton startIcon={<FilterIcon />} className={classes.noactionBtn}
>{gettext('Filter')}</DefaultButton>
<PgIconButton title={gettext('File')} icon={<KeyboardArrowDownIcon />} splitButton
<PgIconButton title={gettext('Filter')} disabled={isDisableCompare} icon={<KeyboardArrowDownIcon />} splitButton
name={MENUS.FILTER} ref={filterRef} onClick={toggleMenu} ></PgIconButton>
</PgButtonGroup>
</Box>

View File

@@ -11,10 +11,10 @@ import PropTypes from 'prop-types';
import React, { useContext, useEffect, useState } from 'react';
import { Box, Grid } from '@material-ui/core';
import InfoRoundedIcon from '@material-ui/icons/InfoRounded';
import HelpIcon from '@material-ui/icons/HelpRounded';
import { makeStyles } from '@material-ui/styles';
import { Box, Grid } from '@mui/material';
import InfoRoundedIcon from '@mui/icons-material/InfoRounded';
import HelpIcon from '@mui/icons-material/HelpRounded';
import { makeStyles } from '@mui/styles';
import gettext from 'sources/gettext';
import url_for from 'sources/url_for';

View File

@@ -14,7 +14,8 @@ import {DividerBox} from 'rc-dock';
import url_for from 'sources/url_for';
import { Box, makeStyles } from '@material-ui/core';
import { Box } from '@mui/material';
import { makeStyles } from '@mui/styles';
import { Results } from './Results';
import { SchemaDiffCompare } from './SchemaDiffCompare';

View File

@@ -6,11 +6,12 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { Box, makeStyles } from '@material-ui/core';
import { Box } from '@mui/material';
import { makeStyles } from '@mui/styles';
import React, { useState, useMemo, useCallback } from 'react';
import PropTypes from 'prop-types';
import HelpIcon from '@material-ui/icons/HelpRounded';
import SearchRoundedIcon from '@material-ui/icons/SearchRounded';
import HelpIcon from '@mui/icons-material/HelpRounded';
import SearchRoundedIcon from '@mui/icons-material/SearchRounded';
import pgAdmin from 'sources/pgadmin';
import gettext from 'sources/gettext';
import url_for from 'sources/url_for';

View File

@@ -20,7 +20,7 @@ import getApiInstance, {callFetch, parseApiError} from '../../../../../static/js
import url_for from 'sources/url_for';
import { PANELS, QUERY_TOOL_EVENTS, CONNECTION_STATUS, MAX_QUERY_LENGTH } from './QueryToolConstants';
import { useInterval } from '../../../../../static/js/custom_hooks';
import { Box } from '@material-ui/core';
import { Box } from '@mui/material';
import { getDatabaseLabel, getTitle, setQueryToolDockerTitle } from '../sqleditor_title';
import gettext from 'sources/gettext';
import NewConnectionDialog from './dialogs/NewConnectionDialog';

View File

@@ -6,11 +6,12 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles, Box, Portal } from '@material-ui/core';
import { Box, Portal } from '@mui/material';
import { makeStyles } from '@mui/styles';
import React, {useContext, useLayoutEffect, useRef} from 'react';
import { DefaultButton, PrimaryButton } from '../../../../../../static/js/components/Buttons';
import CheckRoundedIcon from '@material-ui/icons/CheckRounded';
import CloseIcon from '@material-ui/icons/Close';
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
import CloseIcon from '@mui/icons-material/Close';
import gettext from 'sources/gettext';
import clsx from 'clsx';
import JSONBigNumber from 'json-bignumber';

View File

@@ -8,7 +8,7 @@
//////////////////////////////////////////////////////////////
import React from 'react';
import _ from 'lodash';
import { makeStyles } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import PropTypes from 'prop-types';
import CustomPropTypes from '../../../../../../static/js/custom_prop_types';

View File

@@ -6,18 +6,19 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { Box, makeStyles } from '@material-ui/core';
import { Box } from '@mui/material';
import { makeStyles } from '@mui/styles';
import _ from 'lodash';
import React, {useState, useEffect, useContext, useRef, useLayoutEffect, useMemo} from 'react';
import {Row, useRowSelection} from 'react-data-grid';
import LockIcon from '@material-ui/icons/Lock';
import EditIcon from '@material-ui/icons/Edit';
import LockIcon from '@mui/icons-material/Lock';
import EditIcon from '@mui/icons-material/Edit';
import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';
import * as Editors from './Editors';
import * as Formatters from './Formatters';
import clsx from 'clsx';
import { PgIconButton } from '../../../../../../static/js/components/Buttons';
import MapIcon from '@material-ui/icons/Map';
import MapIcon from '@mui/icons-material/Map';
import { QueryToolEventsContext } from '../QueryToolComponent';
import PropTypes from 'prop-types';
import gettext from 'sources/gettext';

View File

@@ -1,10 +1,10 @@
import React from 'react';
import { useModalStyles } from '../../../../../../static/js/helpers/ModalProvider';
import gettext from 'sources/gettext';
import { Box } from '@material-ui/core';
import { Box } from '@mui/material';
import { DefaultButton, PrimaryButton } from '../../../../../../static/js/components/Buttons';
import CloseIcon from '@material-ui/icons/CloseRounded';
import CheckRoundedIcon from '@material-ui/icons/CheckRounded';
import CloseIcon from '@mui/icons-material/CloseRounded';
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
import HTMLReactParser from 'html-react-parser';
import PropTypes from 'prop-types';

View File

@@ -1,12 +1,13 @@
import React, { useState } from 'react';
import { useModalStyles } from '../../../../../../static/js/helpers/ModalProvider';
import gettext from 'sources/gettext';
import { Box, makeStyles } from '@material-ui/core';
import { Box } from '@mui/material';
import { makeStyles } from '@mui/styles';
import { DefaultButton, PrimaryButton } from '../../../../../../static/js/components/Buttons';
import CloseIcon from '@material-ui/icons/CloseRounded';
import CloseIcon from '@mui/icons-material/CloseRounded';
import HTMLReactParser from 'html-react-parser';
import PropTypes from 'prop-types';
import CheckRounded from '@material-ui/icons/CheckRounded';
import CheckRounded from '@mui/icons-material/CheckRounded';
import { InputCheckbox } from '../../../../../../static/js/components/FormComponents';

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { useModalStyles } from '../../../../../../static/js/helpers/ModalProvider';
import gettext from 'sources/gettext';
import { Box } from '@material-ui/core';
import { Box } from '@mui/material';
import { DefaultButton, PrimaryButton } from '../../../../../../static/js/components/Buttons';
import CloseIcon from '@material-ui/icons/CloseRounded';
import CloseIcon from '@mui/icons-material/CloseRounded';
import HTMLReactParser from 'html-react-parser';
import { CommitIcon, RollbackIcon } from '../../../../../../static/js/components/ExternalIcon';
import PropTypes from 'prop-types';

View File

@@ -6,7 +6,7 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import React from 'react';
import SchemaView from '../../../../../../static/js/SchemaView';
import BaseUISchema from '../../../../../../static/js/SchemaView/base_schema.ui';

View File

@@ -6,7 +6,7 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import React from 'react';
import SchemaView from '../../../../../../static/js/SchemaView';
import BaseUISchema from '../../../../../../static/js/SchemaView/base_schema.ui';

View File

@@ -6,7 +6,7 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import React, { useState } from 'react';
import SchemaView from '../../../../../../static/js/SchemaView';
import BaseUISchema from '../../../../../../static/js/SchemaView/base_schema.ui';

View File

@@ -7,20 +7,20 @@
//
//////////////////////////////////////////////////////////////
import React from 'react';
import { makeStyles } from '@material-ui/styles';
import { Box, CircularProgress, Tooltip } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import { Box, CircularProgress, Tooltip } from '@mui/material';
import { DefaultButton, PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import { ConnectedIcon, DisonnectedIcon, QueryToolIcon } from '../../../../../../static/js/components/ExternalIcon';
import { QueryToolContext } from '../QueryToolComponent';
import { CONNECTION_STATUS, CONNECTION_STATUS_MESSAGE } from '../QueryToolConstants';
import HourglassEmptyRoundedIcon from '@material-ui/icons/HourglassEmptyRounded';
import QueryBuilderRoundedIcon from '@material-ui/icons/QueryBuilderRounded';
import ErrorOutlineRoundedIcon from '@material-ui/icons/ErrorOutlineRounded';
import ReportProblemRoundedIcon from '@material-ui/icons/ReportProblemRounded';
import HourglassEmptyRoundedIcon from '@mui/icons-material/HourglassEmptyRounded';
import QueryBuilderRoundedIcon from '@mui/icons-material/QueryBuilderRounded';
import ErrorOutlineRoundedIcon from '@mui/icons-material/ErrorOutlineRounded';
import ReportProblemRoundedIcon from '@mui/icons-material/ReportProblemRounded';
import { PgMenu, PgMenuItem } from '../../../../../../static/js/components/Menu';
import gettext from 'sources/gettext';
import RotateLeftRoundedIcon from '@material-ui/icons/RotateLeftRounded';
import RotateLeftRoundedIcon from '@mui/icons-material/RotateLeftRounded';
import PropTypes from 'prop-types';
import { useKeyboardShortcuts } from '../../../../../../static/js/custom_hooks';
import CustomPropTypes from '../../../../../../static/js/custom_prop_types';

View File

@@ -8,7 +8,7 @@
//////////////////////////////////////////////////////////////
import React, { useEffect, useRef } from 'react';
import ReactDOMServer from 'react-dom/server';
import { makeStyles } from '@material-ui/styles';
import { makeStyles } from '@mui/styles';
import _ from 'lodash';
import { MapContainer, TileLayer, LayersControl, GeoJSON, useMap } from 'react-leaflet';
import Leaflet, { CRS } from 'leaflet';
@@ -18,7 +18,7 @@ import gettext from 'sources/gettext';
import Theme from 'sources/Theme';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { Box } from '@material-ui/core';
import { Box } from '@mui/material';
import { PANELS } from '../QueryToolConstants';
import { QueryToolContext } from '../QueryToolComponent';

View File

@@ -13,13 +13,13 @@ import gettext from 'sources/gettext';
import PropTypes from 'prop-types';
import url_for from 'sources/url_for';
import Loader from 'sources/components/Loader';
import { makeStyles } from '@material-ui/styles';
import { Box } from '@material-ui/core';
import ShowChartRoundedIcon from '@material-ui/icons/ShowChartRounded';
import ZoomOutMapIcon from '@material-ui/icons/ZoomOutMap';
import SaveAltIcon from '@material-ui/icons/SaveAlt';
import ExpandLessRoundedIcon from '@material-ui/icons/ExpandLessRounded';
import ExpandMoreRoundedIcon from '@material-ui/icons/ExpandMoreRounded';
import { makeStyles } from '@mui/styles';
import { Box } from '@mui/material';
import ShowChartRoundedIcon from '@mui/icons-material/ShowChartRounded';
import ZoomOutMapIcon from '@mui/icons-material/ZoomOutMap';
import SaveAltIcon from '@mui/icons-material/SaveAlt';
import ExpandLessRoundedIcon from '@mui/icons-material/ExpandLessRounded';
import ExpandMoreRoundedIcon from '@mui/icons-material/ExpandMoreRounded';
import { InputSelect } from '../../../../../../static/js/components/FormComponents';
import { DefaultButton, PgButtonGroup, PgIconButton} from '../../../../../../static/js/components/Buttons';
import { LineChart, BarChart, PieChart, DATA_POINT_STYLE, DATA_POINT_SIZE,

View File

@@ -7,20 +7,20 @@
//
//////////////////////////////////////////////////////////////
import React, {useContext, useCallback, useEffect, useState} from 'react';
import { makeStyles } from '@material-ui/styles';
import { Box } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import { Box } from '@mui/material';
import { PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
import FolderRoundedIcon from '@material-ui/icons/FolderRounded';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import SaveRoundedIcon from '@material-ui/icons/SaveRounded';
import StopRoundedIcon from '@material-ui/icons/StopRounded';
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
import FolderRoundedIcon from '@mui/icons-material/FolderRounded';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import SaveRoundedIcon from '@mui/icons-material/SaveRounded';
import StopRoundedIcon from '@mui/icons-material/StopRounded';
import PlayArrowRoundedIcon from '@mui/icons-material/PlayArrowRounded';
import { FilterIcon, CommitIcon, RollbackIcon } from '../../../../../../static/js/components/ExternalIcon';
import EditRoundedIcon from '@material-ui/icons/EditRounded';
import AssessmentRoundedIcon from '@material-ui/icons/AssessmentRounded';
import ExplicitRoundedIcon from '@material-ui/icons/ExplicitRounded';
import FormatListNumberedRoundedIcon from '@material-ui/icons/FormatListNumberedRounded';
import HelpIcon from '@material-ui/icons/HelpRounded';
import EditRoundedIcon from '@mui/icons-material/EditRounded';
import AssessmentRoundedIcon from '@mui/icons-material/AssessmentRounded';
import ExplicitRoundedIcon from '@mui/icons-material/ExplicitRounded';
import FormatListNumberedRoundedIcon from '@mui/icons-material/FormatListNumberedRounded';
import HelpIcon from '@mui/icons-material/HelpRounded';
import {QUERY_TOOL_EVENTS, CONNECTION_STATUS} from '../QueryToolConstants';
import { QueryToolConnectionContext, QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
import { PgMenu, PgMenuDivider, PgMenuItem, usePgMenuGroup } from '../../../../../../static/js/components/Menu';
@@ -457,10 +457,10 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
disabled={!queryToolCtx.params.is_query_tool} accesskey={shortcut_key(queryToolPref.btn_edit_options)}
name="menu-edit" ref={editMenuRef} onClick={toggleMenu} />
</PgButtonGroup>
<PgButtonGroup size="small" color={highlightFilter ? 'primary' : 'default'}>
<PgIconButton title={gettext('Sort/Filter')} icon={<FilterIcon />}
<PgButtonGroup size="small" >
<PgIconButton title={gettext('Sort/Filter')} color={highlightFilter ? 'primary' : 'default'} icon={<FilterIcon />}
onClick={onFilterClick} disabled={buttonsDisabled['filter']} accesskey={shortcut_key(queryToolPref.btn_filter_dialog)}/>
<PgIconButton title={gettext('Filter options')} icon={<KeyboardArrowDownIcon />} splitButton
<PgIconButton title={gettext('Filter options')} color={highlightFilter ? 'primary' : 'default'} icon={<KeyboardArrowDownIcon />} splitButton
disabled={buttonsDisabled['filter']} name="menu-filter" ref={filterMenuRef} accesskey={shortcut_key(queryToolPref.btn_filter_options)}
onClick={toggleMenu} />
</PgButtonGroup>

View File

@@ -6,7 +6,7 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/styles';
import { makeStyles } from '@mui/styles';
import React from 'react';
import { QueryToolEventsContext } from '../QueryToolComponent';
import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';

View File

@@ -6,7 +6,7 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/styles';
import { makeStyles } from '@mui/styles';
import React, {useContext, useCallback, useEffect, useMemo } from 'react';
import { format } from 'sql-formatter';
import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
@@ -258,6 +258,38 @@ export default function Query() {
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_QUERY_CHANGE, ()=>{
change();
});
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_FORMAT_SQL, ()=>{
let selection = true, sql = editor.current?.getSelection();
let sqlEditorPref = preferencesStore.getPreferencesForModule('sqleditor');
/* New library does not support capitalize casing
so if a user has set capitalize casing we will
use preserve casing which is default for the library.
*/
let formatPrefs = {
language: 'postgresql',
keywordCase: sqlEditorPref.keyword_case === 'capitalize' ? 'preserve' : sqlEditorPref.keyword_case,
identifierCase: sqlEditorPref.identifier_case === 'capitalize' ? 'preserve' : sqlEditorPref.identifier_case,
dataTypeCase: sqlEditorPref.data_type_case,
functionCase: sqlEditorPref.function_case,
logicalOperatorNewline: sqlEditorPref.logical_operator_new_line,
expressionWidth: sqlEditorPref.expression_width,
linesBetweenQueries: sqlEditorPref.lines_between_queries,
tabWidth: sqlEditorPref.tab_size,
useTabs: !sqlEditorPref.use_spaces,
denseOperators: !sqlEditorPref.spaces_around_operators,
newlineBeforeSemicolon: sqlEditorPref.new_line_before_semicolon
};
if(sql == '') {
sql = editor.current.getValue();
selection = false;
}
let formattedSql = format(sql,formatPrefs);
if(selection) {
editor.current.replaceSelection(formattedSql, 'around');
} else {
editor.current.setValue(formattedSql);
}
});
eventBus.registerListener(QUERY_TOOL_EVENTS.EDITOR_TOGGLE_CASE, ()=>{
let selectedText = editor.current?.getSelection();
if (!selectedText) return;

View File

@@ -6,20 +6,20 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/styles';
import { makeStyles } from '@mui/styles';
import React, { useContext } from 'react';
import { PANELS, QUERY_TOOL_EVENTS, MAX_QUERY_LENGTH } from '../QueryToolConstants';
import gettext from 'sources/gettext';
import pgAdmin from 'sources/pgadmin';
import _ from 'lodash';
import clsx from 'clsx';
import { Box, Grid, List, ListItem, ListSubheader } from '@material-ui/core';
import { Box, Grid, List, ListItem, ListSubheader } from '@mui/material';
import url_for from 'sources/url_for';
import { QueryToolConnectionContext, QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
import moment from 'moment';
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
import AssessmentRoundedIcon from '@material-ui/icons/AssessmentRounded';
import ExplicitRoundedIcon from '@material-ui/icons/ExplicitRounded';
import PlayArrowRoundedIcon from '@mui/icons-material/PlayArrowRounded';
import AssessmentRoundedIcon from '@mui/icons-material/AssessmentRounded';
import ExplicitRoundedIcon from '@mui/icons-material/ExplicitRounded';
import { SaveDataIcon, CommitIcon, RollbackIcon, ViewDataIcon } from '../../../../../../static/js/components/ExternalIcon';
import { InputSwitch } from '../../../../../../static/js/components/FormComponents';
import CodeMirror from '../../../../../../static/js/components/ReactCodeMirror';
@@ -512,7 +512,7 @@ export function QueryHistory() {
</Box>
</Box>
<Box flexGrow="1" overflow="auto" className={classes.listRoot}>
<List innerRef={listRef} className={classes.root} subheader={<li />} tabIndex="0" onKeyDown={onKeyPressed}>
<List ref={listRef} className={classes.root} subheader={<li />} tabIndex="0" onKeyDown={onKeyPressed}>
{qhu.current.getGroups().map(([groupKey, groupHeader]) => (
<ListItem key={`section-${groupKey}`} className={classes.removePadding}>
<List className={classes.removePadding}>

View File

@@ -15,7 +15,7 @@ import getApiInstance, { parseApiError } from '../../../../../../static/js/api_i
import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
import gettext from 'sources/gettext';
import Loader from 'sources/components/Loader';
import { Box } from '@material-ui/core';
import { Box } from '@mui/material';
import { ResultSetToolbar } from './ResultSetToolbar';
import { LayoutDockerContext } from '../../../../../../static/js/helpers/Layout';
import { GeometryViewer } from './GeometryViewer';
@@ -25,7 +25,7 @@ import { getBrowser } from '../../../../../../static/js/utils';
import CopyData from '../QueryToolDataGrid/CopyData';
import moment from 'moment';
import ConfirmSaveContent from '../../../../../../static/js/Dialogs/ConfirmSaveContent';
import { makeStyles } from '@material-ui/styles';
import { makeStyles } from '@mui/styles';
import EmptyPanelMessage from '../../../../../../static/js/components/EmptyPanelMessage';
import { GraphVisualiser } from './GraphVisualiser';
import { usePgAdmin } from '../../../../../../static/js/BrowserComponent';

View File

@@ -7,16 +7,16 @@
//
//////////////////////////////////////////////////////////////
import React, {useContext, useCallback, useEffect, useState} from 'react';
import { makeStyles } from '@material-ui/styles';
import { Box } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import { Box } from '@mui/material';
import { PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import PlaylistAddRoundedIcon from '@material-ui/icons/PlaylistAddRounded';
import FileCopyRoundedIcon from '@material-ui/icons/FileCopyRounded';
import DeleteRoundedIcon from '@material-ui/icons/DeleteRounded';
import TimelineRoundedIcon from '@material-ui/icons/TimelineRounded';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import PlaylistAddRoundedIcon from '@mui/icons-material/PlaylistAddRounded';
import FileCopyRoundedIcon from '@mui/icons-material/FileCopyRounded';
import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded';
import TimelineRoundedIcon from '@mui/icons-material/TimelineRounded';
import { PasteIcon, SaveDataIcon } from '../../../../../../static/js/components/ExternalIcon';
import GetAppRoundedIcon from '@material-ui/icons/GetAppRounded';
import GetAppRoundedIcon from '@mui/icons-material/GetAppRounded';
import {QUERY_TOOL_EVENTS} from '../QueryToolConstants';
import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
import { PgMenu, PgMenuItem } from '../../../../../../static/js/components/Menu';

View File

@@ -8,8 +8,8 @@
//
//////////////////////////////////////////////////////////////
import React, { useEffect, useState, useContext } from 'react';
import { makeStyles } from '@material-ui/styles';
import { Box } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import { Box } from '@mui/material';
import clsx from 'clsx';
import _ from 'lodash';
import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';

View File

@@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
import React from 'react';
import { makeStyles } from '@material-ui/core';
import { makeStyles } from '@mui/styles';
import SchemaView from '../../../../static/js/SchemaView';
import BaseUISchema from '../../../../static/js/SchemaView/base_schema.ui';
import pgAdmin from 'sources/pgadmin';

View File

@@ -1,31 +0,0 @@
.bg-user-invalid {
@extend .bg-warning-light;
}
.user_container {
border-bottom: $panel-border;
height: 100%;
min-height: 0;
margin-top: $title-height;
}
.user_management {
height: 100%;
overflow: hidden;
& .navtab-inline-controls {
position: absolute;
width: 100%;
}
}
.user_management .search_users form {
margin: 0;
}
.user_management table {
display: block;
height: 100%;
overflow: auto;
border: 0 none;
}