mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-12-22 15:13:42 -06:00
Significant changes to use ReactJS extensively.
1. Replace the current layout library wcDocker with ReactJS based rc-dock. #6479 2. Have close buttons on individual panel tabs instead of common. #2821 3. Changes in the context menu on panel tabs - Add close, close all and close others menu items. #5394 4. Allow closing all the tabs, including SQL and Properties. #4733 5. Changes in docking behaviour of different tabs based on user requests and remove lock layout menu. 6. Fix an issue where the scroll position of panels was not remembered on Firefox. #2986 7. Reset layout now will not require page refresh and is done spontaneously. 8. Use the zustand store for storing preferences instead of plain JS objects. This will help reflecting preferences immediately. 9. The above fix incorrect format (no indent) of SQL stored functions/procedures. #6720 10. New version check is moved to an async request now instead of app start to improve startup performance. 11. Remove jQuery and Bootstrap completely. 12. Replace jasmine and karma test runner with jest. Migrate all the JS test cases to jest. This will save time in writing and debugging JS tests. 13. Other important code improvements and cleanup.
This commit is contained in:
parent
6d555645e9
commit
862f101772
2
.github/workflows/run-javascript-tests.yml
vendored
2
.github/workflows/run-javascript-tests.yml
vendored
@ -36,4 +36,4 @@ jobs:
|
||||
- name: Run the tests
|
||||
run: |
|
||||
cd web
|
||||
yarn run test:karma-once
|
||||
yarn run test:js-once
|
||||
|
@ -55,7 +55,8 @@ RUN export CPPFLAGS="-DPNG_ARM_NEON_OPT=0" && \
|
||||
.[^.]* \
|
||||
babel.cfg \
|
||||
webpack.* \
|
||||
karma.conf.js \
|
||||
jest.config.js \
|
||||
babel.* \
|
||||
./pgadmin/static/js/generated/.cache
|
||||
|
||||
#########################################################################
|
||||
|
6
Makefile
6
Makefile
@ -42,7 +42,7 @@ linter:
|
||||
cd web && yarn run linter
|
||||
|
||||
check: install-node bundle linter check-pep8
|
||||
cd web && yarn run karma start --single-run && python regression/runtests.py
|
||||
cd web && yarn run test:js-once && python regression/runtests.py
|
||||
|
||||
check-audit:
|
||||
cd web && yarn run audit
|
||||
@ -77,10 +77,10 @@ check-feature: install-node bundle
|
||||
cd web && python regression/runtests.py --pkg feature_tests
|
||||
|
||||
check-js: install-node linter
|
||||
cd web && yarn run karma start --single-run
|
||||
cd web && yarn run test:js-once
|
||||
|
||||
check-js-coverage:
|
||||
cd web && yarn run test:karma-coverage
|
||||
cd web && yarn run test:js-coverage
|
||||
|
||||
# Include all clean sub-targets in clean
|
||||
clean: clean-appbundle clean-debian clean-dist clean-docs clean-node clean-pip clean-redhat clean-src
|
||||
|
35
README.md
35
README.md
@ -1,7 +1,7 @@
|
||||
# pgAdmin 4
|
||||
# pgAdmin 4
|
||||
|
||||
pgAdmin 4 is a rewrite of the popular pgAdmin3 management tool for the
|
||||
PostgreSQL (http://www.postgresql.org) database.
|
||||
PostgreSQL (http://www.postgresql.org) database.
|
||||
|
||||
In the following documentation and examples, *$PGADMIN4_SRC/* is used to denote
|
||||
the top-level directory of a copy of the pgAdmin source tree, either from a
|
||||
@ -9,9 +9,8 @@ tarball or a git checkout.
|
||||
|
||||
## Architecture
|
||||
|
||||
pgAdmin 4 is written as a web application in Python, using jQuery and Bootstrap
|
||||
for the client side processing and UI. On the server side, Flask is being
|
||||
utilised.
|
||||
pgAdmin 4 is written as a web application with Python(Flask) on the server side
|
||||
and ReactJS, HTML5 with CSS for the client side processing and UI.
|
||||
|
||||
Although developed using web technologies, pgAdmin 4 can be deployed either on
|
||||
a web server using a browser, or standalone on a workstation. The runtime/
|
||||
@ -22,7 +21,7 @@ which will execute the Python server and display the UI.
|
||||
|
||||
To build the runtime, the following packages must be installed:
|
||||
|
||||
* NodeJS 12+
|
||||
* NodeJS 16+
|
||||
* Yarn
|
||||
|
||||
Change into the runtime directory, and run *yarn install*. This will install the
|
||||
@ -59,20 +58,20 @@ simple - adapt as required for your distribution:
|
||||
```bash
|
||||
$ python3 -m venv venv
|
||||
```
|
||||
|
||||
|
||||
2. Now activate the virtual environment:
|
||||
|
||||
```bash
|
||||
$ source venv/bin/activate
|
||||
```
|
||||
|
||||
|
||||
3. Some of the components used by pgAdmin require a very recent version of *pip*,
|
||||
so update that to the latest:
|
||||
|
||||
|
||||
```bash
|
||||
$ pip install --upgrade pip
|
||||
```
|
||||
|
||||
|
||||
4. Ensure that a PostgreSQL installation's bin/ directory is in the path (so
|
||||
pg_config can be found for building psycopg3), and install the required
|
||||
packages:
|
||||
@ -80,20 +79,20 @@ simple - adapt as required for your distribution:
|
||||
```bash
|
||||
(venv) $ PATH=$PATH:/usr/local/pgsql/bin pip install -r $PGADMIN4_SRC/requirements.txt
|
||||
```
|
||||
|
||||
|
||||
If you are planning to run the regression tests, you also need to install
|
||||
additional requirements from web/regression/requirements.txt:
|
||||
|
||||
```bash
|
||||
(venv) $ pip install -r $PGADMIN4_SRC/web/regression/requirements.txt
|
||||
```
|
||||
|
||||
|
||||
5. Create a local configuration file for pgAdmin. Edit
|
||||
$PGADMIN4_SRC/web/config_local.py and add any desired configuration options
|
||||
(use the config.py file as a reference - any settings duplicated in
|
||||
config_local.py will override those in config.py). A typical development
|
||||
configuration may look like:
|
||||
|
||||
|
||||
```python
|
||||
from config import *
|
||||
|
||||
@ -126,7 +125,7 @@ simple - adapt as required for your distribution:
|
||||
'pgadmin4-server.db'
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
This configuration allows easy switching between server and desktop modes
|
||||
for testing.
|
||||
|
||||
@ -137,13 +136,13 @@ simple - adapt as required for your distribution:
|
||||
```bash
|
||||
(venv) $ python3 $PGADMIN4_SRC/web/setup.py
|
||||
```
|
||||
|
||||
|
||||
or by starting pgAdmin 4:
|
||||
|
||||
```bash
|
||||
(venv) $ python3 $PGADMIN4_SRC/web/pgAdmin4.py
|
||||
```
|
||||
|
||||
|
||||
Whilst it is possible to automatically run setup in desktop mode by running
|
||||
the runtime, that will not work in server mode as the runtime doesn't allow
|
||||
command line interaction with the setup program.
|
||||
@ -228,7 +227,7 @@ To build a source tarball:
|
||||
(venv) $ make src
|
||||
```
|
||||
|
||||
To build a PIP Wheel, activate either a Python 3 virtual environment, configured
|
||||
To build a PIP Wheel, activate either a Python 3 virtual environment, configured
|
||||
with all the required packages, and then run:
|
||||
|
||||
```bash
|
||||
@ -266,7 +265,7 @@ See https://www.pgadmin.org/support/ for support options.
|
||||
|
||||
If you would like to report a security issue with pgAdmin, please email
|
||||
**security (at) pgadmin (dot) org**.
|
||||
|
||||
|
||||
Note that this address should only be used for reporting security issues
|
||||
that you believe you've found in the design or code of pgAdmin, pgAgent,
|
||||
and the pgAdmin website. It should not be used to ask security questions.
|
||||
|
@ -5,7 +5,7 @@
|
||||
**********************
|
||||
|
||||
The bulk of pgAdmin is a Python web application written using the Flask framework
|
||||
on the backend, and HTML5 with CSS3, Bootstrap and jQuery on the front end. A
|
||||
on the backend, and HTML5 with CSS3,ReactJS on the front end. A
|
||||
desktop runtime is also included for users that prefer a desktop application to
|
||||
a web application, which is written using NWjs (Node Webkit).
|
||||
|
||||
|
@ -64,7 +64,7 @@ All HTML must be HTML 5 compliant.
|
||||
Javascript
|
||||
**********
|
||||
|
||||
Client-side code is written in Javascript using jQuery and various plugins.
|
||||
Client-side code is written in Javascript using ReactJS and various plugins.
|
||||
Whilst much of the code is rendered from static files, there is also code that
|
||||
is rendered from templates using Jinja2 (often to inject the users settings) or
|
||||
constructed on the fly from module hooks.
|
||||
|
@ -25,9 +25,6 @@ Use the *File* menu to access the following options:
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------+
|
||||
| *Reset Layout* | If you have modified the workspace, click to restore the default layout. |
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------+
|
||||
| *Lock Layout* | Click to open a submenu to select the level for locking the UI layout |
|
||||
| | This can also be changed from browser display :ref:`preferences <preferences>` |
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------+
|
||||
| *Runtime* | Click to open a submenu to Configure, View Log and Zoom settings. Only visible when pgAdmin4 runs in |
|
||||
| | desktop mode. To know more about runtime menu :ref:`click here <desktop_deployment>` |
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------+
|
||||
|
@ -46,19 +46,6 @@ Use the fields on the *Display* panel to specify general display preferences:
|
||||
* When the *Hide shared servers?* switch is set to *True*, the client will hide
|
||||
all the shared servers from the object explorer.
|
||||
|
||||
* Use the *Lock layout* field to lock the UI layout at different levels. This
|
||||
can also be changed from File menu on the :ref:`menu bar <menu_bar>`
|
||||
|
||||
+---------------------+-------------------------------------------------------------------+
|
||||
| Option | Action |
|
||||
+=====================+===================================================================+
|
||||
| *None* | No locking. Every panel is resizable and dockable. |
|
||||
+---------------------+-------------------------------------------------------------------+
|
||||
| *Prevent docking* | This will disable the docking/undocking of the panels |
|
||||
+---------------------+-------------------------------------------------------------------+
|
||||
| *Full* | This will disable resizing, docking/undocking of the panels |
|
||||
+---------------------+-------------------------------------------------------------------+
|
||||
|
||||
* When the *Show empty object collections?* switch is turned off, then all object
|
||||
collections which are empty will be hidden from browser tree.
|
||||
* When the *Show system objects?* switch is set to *True*, the client will
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
html{
|
||||
background-color: #fff;
|
||||
|
||||
|
||||
}
|
||||
|
||||
body {
|
||||
@ -10,6 +10,7 @@ body {
|
||||
font-family: Roboto, Helvetica Neue, -apple-system, BlinkMacSystemFont, Segoe UI, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background-color: #fff;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
div.body {
|
||||
|
@ -224,7 +224,7 @@ _copy_code() {
|
||||
cp "${SOURCEDIR}/pkg/linux/config_distro.py" "${SERVERROOT}/usr/${APP_NAME}/web/"
|
||||
cd "${SERVERROOT}/usr/${APP_NAME}/web/" || exit
|
||||
rm -f pgadmin4.db config_local.*
|
||||
rm -rf karma.conf.js package.json node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
|
||||
rm -rf jest.config.js babel.* package.json node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
|
||||
find . -name "tests" -type d -print0 | xargs -0 rm -rf
|
||||
find . -name "feature_tests" -type d -print0 | xargs -0 rm -rf
|
||||
find . -name "__pycache__" -type d -print0 | xargs -0 rm -rf
|
||||
|
@ -298,7 +298,7 @@ _complete_bundle() {
|
||||
cp -r "${SOURCE_DIR}/web" "${BUNDLE_DIR}/Contents/Resources/"
|
||||
cd "${BUNDLE_DIR}/Contents/Resources/web" || exit
|
||||
rm -f pgadmin4.db config_local.*
|
||||
rm -rf karma.conf.js package.json .yarn* yarn* .editorconfig .eslint* node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
|
||||
rm -rf jest.config.js babel.* package.json .yarn* yarn* .editorconfig .eslint* node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
|
||||
find . -name "tests" -type d -print0 | xargs -0 rm -rf
|
||||
find . -name "feature_tests" -type d -print0 | xargs -0 rm -rf
|
||||
find . -name "__pycache__" -type d -print0 | xargs -0 rm -rf
|
||||
|
@ -12,7 +12,6 @@ module.exports = {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'amd': true,
|
||||
'jasmine': true,
|
||||
},
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
|
@ -12,7 +12,6 @@ module.exports = {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'amd': true,
|
||||
'jasmine': true,
|
||||
},
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
@ -41,10 +40,11 @@ module.exports = {
|
||||
'plugins': [
|
||||
'react',
|
||||
'@babel',
|
||||
'jest'
|
||||
],
|
||||
'overrides': [
|
||||
{
|
||||
'files': ['**/*.ts', '**/*.tsx'],
|
||||
'files': ['**/*.{ts,tsx}'],
|
||||
'plugins': [
|
||||
'@typescript-eslint',
|
||||
],
|
||||
@ -55,10 +55,20 @@ module.exports = {
|
||||
'@typescript-eslint/no-this-alias': ['off'],
|
||||
}
|
||||
},
|
||||
{
|
||||
'files': ['**/*{spec,test}.{js,jsx}', './regression/javascript/**/*.{js}'],
|
||||
'extends': ['eslint:recommended'],
|
||||
'env': {
|
||||
'jest': true
|
||||
}
|
||||
},
|
||||
],
|
||||
'globals': {
|
||||
'_': true,
|
||||
'module': true,
|
||||
'__dirname': true,
|
||||
'global': true,
|
||||
'jest': true
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
|
4
web/babel.config.json
Normal file
4
web/babel.config.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"presets": [["@babel/preset-env", {"modules": "commonjs", "useBuiltIns": "usage", "corejs": 3}], "@babel/preset-react", "@babel/preset-typescript"],
|
||||
"plugins": ["@babel/plugin-proposal-class-properties", "@babel/proposal-object-rest-spread", "@babel/plugin-transform-runtime"]
|
||||
}
|
57
web/jest.config.js
Normal file
57
web/jest.config.js
Normal file
@ -0,0 +1,57 @@
|
||||
const webpackShimAlias = require('./webpack.shim').resolveAlias;
|
||||
|
||||
const webpackAliasToJestModules = ()=>{
|
||||
const ret = {
|
||||
'\\.svg': '<rootDir>/regression/javascript/__mocks__/svg.js'
|
||||
};
|
||||
Object.keys(webpackShimAlias).forEach((an)=>{
|
||||
// eg - sources: ./pgadmin/static/js/ to '^sources/(.*)$': '<rootDir>/pgadmin/static/js/$1'
|
||||
let ap = webpackShimAlias[an].replace(__dirname, '<rootDir>');
|
||||
if(ap.endsWith('/')) {
|
||||
ret[`^${an}/(.*)$`] = ap + '$1';
|
||||
return;
|
||||
}
|
||||
ret[`^${an}$`] = ap;
|
||||
});
|
||||
|
||||
// Overrides
|
||||
ret['^translations$'] = '<rootDir>/regression/javascript/fake_translations';
|
||||
ret['^pgadmin.browser.messages$'] = '<rootDir>/regression/javascript/fake_messages';
|
||||
ret['^pgadmin.browser.endpoints$'] = '<rootDir>/regression/javascript/fake_endpoints';
|
||||
ret['^pgadmin.browser.translations$'] = '<rootDir>/regression/javascript/fake_translations';
|
||||
ret['^pgadmin.user_management.current_user$'] = '<rootDir>/regression/javascript/fake_current_user';
|
||||
ret['^pgadmin.server.supported_servers$'] = '<rootDir>/regression/javascript/fake_supported_servers';
|
||||
|
||||
const sources = ret['^sources/(.*)$'];
|
||||
delete ret['^sources/(.*)$'];
|
||||
|
||||
ret['^sources/pgadmin$'] = '<rootDir>/regression/javascript/fake_pgadmin';
|
||||
ret['^sources/gettext$'] = '<rootDir>/regression/javascript/fake_gettext';
|
||||
ret['^sources/(.*)$'] = sources;
|
||||
|
||||
// Only for tests
|
||||
ret['^pgadmin.schema.dir/(.*)$'] = '<rootDir>/pgadmin/browser/server_groups/servers/databases/schemas/static/js/$1';
|
||||
ret['^browser/(.*)$'] = '<rootDir>/pgadmin/browser/static/js/$1';
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
'roots': ['<rootDir>/pgadmin/', '<rootDir>/regression/javascript'],
|
||||
'moduleFileExtensions': ['js', 'jsx', 'ts', 'tsx'],
|
||||
'moduleNameMapper': webpackAliasToJestModules(),
|
||||
'transform': {
|
||||
'^.+\\.(js|jsx|mjs|cjs|ts|tsx)$': 'babel-jest',
|
||||
},
|
||||
'setupFilesAfterEnv': [
|
||||
'<rootDir>/regression/javascript/setup-jest.js',
|
||||
],
|
||||
'testMatch': [
|
||||
'<rootDir>/regression/javascript/**/*{spec,test}.{js,jsx,ts,tsx}'
|
||||
],
|
||||
'testEnvironment': 'jsdom',
|
||||
'transformIgnorePatterns': [
|
||||
'[/\\\\]node_modules[/\\\\](?!react-dnd|dnd-core|@react-dnd).+\\.(js|jsx|mjs|cjs|ts|tsx)$',
|
||||
'^.+\\.module\\.(css|sass|scss)$'
|
||||
]
|
||||
};
|
@ -1,106 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
// Karma configuration
|
||||
const webpackConfig = require('./webpack.test.config.js');
|
||||
const isDocker = require('is-docker')();
|
||||
const webpack = require('webpack');
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
frameworks: ['jasmine', 'source-map-support'],
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
plugins: [
|
||||
'karma-webpack',
|
||||
'karma-chrome-launcher',
|
||||
'karma-jasmine',
|
||||
'karma-jasmine-html-reporter',
|
||||
'karma-source-map-support',
|
||||
'karma-sourcemap-loader',
|
||||
'karma-coverage',
|
||||
new webpack.SourceMapDevToolPlugin({
|
||||
/*
|
||||
* filename: null, // if no value is provided the sourcemap is inlined
|
||||
*/
|
||||
filename: '[name].js.map',
|
||||
test: /\.js$/i, // process .js files only
|
||||
}),
|
||||
],
|
||||
files: [
|
||||
{pattern: 'pgadmin/static/**/*.js', included: false, watched: true},
|
||||
{pattern: 'pgadmin/browser/static/js/**/*.js', included: false, watched: true},
|
||||
{pattern: 'regression/javascript/**/*.js', watched: true},
|
||||
],
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [
|
||||
'pgadmin/static/vendor/**/*[Tt]est.js',
|
||||
'pgadmin/static/vendor/**/*[Ss]pec.js',
|
||||
],
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
'pgadmin/**/js/**/*.js?': ['sourcemap'],
|
||||
'regression/javascript/**/*.js': ['webpack', 'sourcemap'],
|
||||
},
|
||||
|
||||
// optionally, configure the reporter
|
||||
coverageReporter: {
|
||||
reporters: [
|
||||
// reporters not supporting the `file` property
|
||||
{ type: 'html', subdir: 'report-html' },
|
||||
{ type: 'lcovonly', subdir: 'report-lcov' },
|
||||
],
|
||||
dir : 'coverage/',
|
||||
includeAllSources: true,
|
||||
},
|
||||
|
||||
webpack: webpackConfig,
|
||||
webpackMiddleware: {
|
||||
stats: 'errors-only',
|
||||
},
|
||||
|
||||
port: 9876,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_WARN,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: true,
|
||||
usePolling: true,
|
||||
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
customLaunchers: {
|
||||
ChromeCustom: {
|
||||
base: 'ChromeHeadless',
|
||||
// We must disable the Chrome sandbox when running Chrome inside Docker (Chrome's sandbox needs
|
||||
// more permissions than Docker allows by default)
|
||||
flags: isDocker ? ['--no-sandbox'] : [],
|
||||
},
|
||||
},
|
||||
browsers: ['ChromeCustom'],
|
||||
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: false,
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: Infinity,
|
||||
});
|
||||
};
|
@ -12,6 +12,7 @@
|
||||
"@babel/eslint-plugin": "^7.17.7",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.10.1",
|
||||
"@babel/plugin-syntax-jsx": "^7.16.0",
|
||||
"@babel/plugin-transform-runtime": "^7.22.15",
|
||||
"@babel/preset-env": "^7.10.2",
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@emotion/core": "^10.0.14",
|
||||
@ -20,44 +21,35 @@
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@emotion/utils": "^1.0.0",
|
||||
"@svgr/webpack": "^6.2.1",
|
||||
"@testing-library/jest-dom": "^6.1.2",
|
||||
"@testing-library/react": "12",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/jest": "^29.5.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.9",
|
||||
"@typescript-eslint/parser": "^5.57.0",
|
||||
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
|
||||
"autoprefixer": "^10.2.4",
|
||||
"axios-mock-adapter": "^1.17.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"browserify": "^17.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"core-js": "^3.2.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^6.7.2",
|
||||
"css-minimizer-webpack-plugin": "^5.0.0",
|
||||
"enzyme": "^3.11.0",
|
||||
"eslint": "^8.37.0",
|
||||
"eslint-plugin-jest": "^27.4.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"exports-loader": "^4.0.0",
|
||||
"html-react-parser": "^4.2.0",
|
||||
"html-react-parser": "^4.2.2",
|
||||
"image-minimizer-webpack-plugin": "^3.8.2",
|
||||
"imagemin": "^8.0.1",
|
||||
"imagemin-mozjpeg": "^10.0.0",
|
||||
"imagemin-optipng": "^8.0.0",
|
||||
"imports-loader": "^4.0.1",
|
||||
"is-docker": "^2.1.1",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jasmine-core": "3.10.1",
|
||||
"jasmine-enzyme": "^7.1.2",
|
||||
"karma": "^6.3.15",
|
||||
"karma-babel-preprocessor": "^8.0.0",
|
||||
"karma-browserify": "^8.0.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-jasmine": "^4.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.0",
|
||||
"karma-requirejs": "~1.1.0",
|
||||
"karma-source-map-support": "^1.4.0",
|
||||
"karma-sourcemap-loader": "^0.4.0",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"jest": "^29.6.4",
|
||||
"jest-environment-jsdom": "^29.6.4",
|
||||
"loader-utils": "^3.2.1",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"postcss-loader": "^7.1.0",
|
||||
@ -87,6 +79,7 @@
|
||||
"@date-io/core": "^1.3.6",
|
||||
"@date-io/date-fns": "1.x",
|
||||
"@emotion/sheet": "^1.0.1",
|
||||
"@fortawesome/fontawesome-free": "latest",
|
||||
"@material-ui/core": "4.12.4",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "4.0.0-alpha.61",
|
||||
@ -103,7 +96,6 @@
|
||||
"axios": "^1.4.0",
|
||||
"babelify": "~10.0.0",
|
||||
"bignumber.js": "^9.0.1",
|
||||
"bootstrap": "^4.3.1",
|
||||
"brace": "^0.11.1",
|
||||
"browserfs": "^1.4.3",
|
||||
"chart.js": "^3.0.0",
|
||||
@ -120,11 +112,8 @@
|
||||
"immutability-helper": "^3.0.0",
|
||||
"insert-if": "^1.1.0",
|
||||
"ip-address": "^7.1.0",
|
||||
"jquery": "^3.6.0",
|
||||
"jquery-contextmenu": "^2.9.2",
|
||||
"json-bignumber": "^1.0.1",
|
||||
"jsoneditor": "^9.5.4",
|
||||
"karma-coverage": "^2.0.3",
|
||||
"leaflet": "^1.5.1",
|
||||
"lodash": "4.*",
|
||||
"ml-matrix": "^6.5.0",
|
||||
@ -150,10 +139,11 @@
|
||||
"react-dom": "^17.0.1",
|
||||
"react-draggable": "^4.4.4",
|
||||
"react-dropzone": "^14.2.1",
|
||||
"react-frame-component": "^5.2.6",
|
||||
"react-leaflet": "^3.2.2",
|
||||
"react-new-window": "^1.0.1",
|
||||
"react-resize-detector": "^9.1.0",
|
||||
"react-rnd": "^10.3.5",
|
||||
"react-router-dom": "^6.2.2",
|
||||
"react-select": "^5.7.2",
|
||||
"react-table": "^7.6.3",
|
||||
"react-timer-hook": "^3.0.5",
|
||||
@ -163,16 +153,15 @@
|
||||
"socket.io-client": "^4.5.0",
|
||||
"split.js": "^1.5.10",
|
||||
"styled-components": "^5.2.1",
|
||||
"tempusdominus-core": "^5.19.3",
|
||||
"uplot": "^1.6.24",
|
||||
"uplot-react": "^1.1.4",
|
||||
"valid-filename": "^2.0.1",
|
||||
"webcabin-docker": "https://github.com/pgadmin-org/wcdocker#460fc6d90ba170bb177faaa8277f5fbb8279522a",
|
||||
"wkx": "^0.5.0",
|
||||
"xterm": "^4.11.0",
|
||||
"xterm-addon-fit": "^0.5.0",
|
||||
"xterm-addon-search": "^0.8.0",
|
||||
"xterm-addon-web-links": "^0.4.0"
|
||||
"xterm-addon-web-links": "^0.4.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"linter": "yarn eslint --no-eslintrc -c .eslintrc.js --ext .js --ext .jsx --ext .ts --ext .tsx .",
|
||||
@ -181,12 +170,12 @@
|
||||
"bundle:watch": "yarn run linter && yarn run webpacker:watch",
|
||||
"bundle:dev": "yarn run linter && yarn run webpacker",
|
||||
"bundle:analyze": "cross-env NODE_ENV=production ANALYZE=true yarn run bundle:dev",
|
||||
"bundle": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=3072 yarn run bundle:dev",
|
||||
"test:karma-once": "yarn run linter && yarn run karma start --single-run",
|
||||
"test:karma": "yarn run linter && yarn run karma start",
|
||||
"test:karma-coverage": "yarn run test:karma-once --reporters coverage,progress",
|
||||
"bundle": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=2048 yarn run bundle:dev",
|
||||
"test:js-once": "yarn run linter && yarn run jest --maxWorkers=50%",
|
||||
"test:js": "yarn run test:js-once --watch",
|
||||
"test:js-coverage": "yarn run test:js-once --collect-coverage",
|
||||
"test:feature": "yarn run bundle && python regression/runtests.py --pkg feature_tests",
|
||||
"test": "yarn run test:karma-once && yarn run bundle && python regression/runtests.py",
|
||||
"test": "yarn run test:js-once && yarn run bundle && python regression/runtests.py",
|
||||
"pep8": "pycodestyle --config=../.pycodestyle ../docs && pycodestyle --config=../.pycodestyle ../pkg && pycodestyle --config=../.pycodestyle ../tools && pycodestyle --config=../.pycodestyle ../web",
|
||||
"auditjs-html": "yarn audit --json | yarn run yarn-audit-html --output ../auditjs.html",
|
||||
"auditjs": "yarn audit --groups dependencies",
|
||||
|
@ -165,13 +165,6 @@ class PgAdmin(Flask):
|
||||
|
||||
return scripts
|
||||
|
||||
@property
|
||||
def panels(self):
|
||||
panels = []
|
||||
for module in self.submodules:
|
||||
panels.extend(module.get_panels())
|
||||
return panels
|
||||
|
||||
@property
|
||||
def menu_items(self):
|
||||
from operator import attrgetter
|
||||
|
@ -17,8 +17,8 @@ import { makeStyles } from '@material-ui/styles';
|
||||
import { InputText } from '../../../static/js/components/FormComponents';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import { copyToClipboard } from '../../../static/js/clipboard';
|
||||
import Notify from '../../../static/js/helpers/Notifier';
|
||||
import { useDelayedCaller } from '../../../static/js/custom_hooks';
|
||||
import { usePgAdmin } from '../../../static/js/BrowserComponent';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
@ -44,6 +44,7 @@ export default function AboutComponent() {
|
||||
const revertCopiedText = useDelayedCaller(()=>{
|
||||
setCopyText(gettext('Copy'));
|
||||
});
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
useEffect(() => {
|
||||
const about_url = url_for('about.index');
|
||||
@ -52,7 +53,7 @@ export default function AboutComponent() {
|
||||
api.get(about_url).then((res)=>{
|
||||
setAboutData(res.data.data);
|
||||
}).catch((err)=>{
|
||||
Notify.error(err);
|
||||
pgAdmin.Browser.notifier.error(err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
import React from 'react';
|
||||
import gettext from 'sources/gettext';
|
||||
import Notify from '../../../static/js/helpers/Notifier';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import AboutComponent from './AboutComponent';
|
||||
import current_user from 'pgadmin.user_management.current_user';
|
||||
@ -41,9 +40,9 @@ class About {
|
||||
}
|
||||
|
||||
// Render About component
|
||||
Notify.showModal(gettext('About %s', pgAdmin.Browser.utils.app_name), () => {
|
||||
pgAdmin.Browser.notifier.showModal(gettext('About %s', pgAdmin.Browser.utils.app_name), () => {
|
||||
return <AboutComponent />;
|
||||
}, { isFullScreen: false, isResizeable: true, showFullScreen: true,
|
||||
}, { isFullScreen: false, isResizeable: true, showFullScreen: true,
|
||||
isFullWidth: true, dialogWidth: dlgWidth, dialogHeight: dlgHeight, minHeight: dlgHeight
|
||||
});
|
||||
}
|
||||
@ -53,4 +52,4 @@ pgAdmin.About = About.getInstance();
|
||||
|
||||
module.exports = {
|
||||
About: About,
|
||||
};
|
||||
};
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import url_for from 'sources/url_for';
|
||||
import userInfo from 'pgadmin.user_management.current_user';
|
||||
import pgConst from 'pgadmin.browser.constants';
|
||||
import {AUTH_METHODS} from 'pgadmin.browser.constants';
|
||||
|
||||
function fetch_ticket() {
|
||||
// Fetch the Kerberos Updated ticket through SPNEGO
|
||||
@ -52,7 +52,7 @@ function fetch_ticket_lifetime () {
|
||||
function validate_kerberos_ticket() {
|
||||
// Ping pgAdmin server every 10 seconds
|
||||
// to fetch the Kerberos ticket lifetime left
|
||||
if (userInfo['current_auth_source'] != pgConst['KERBEROS']) return;
|
||||
if (userInfo['current_auth_source'] != AUTH_METHODS['KERBEROS']) return;
|
||||
|
||||
return setInterval(function() {
|
||||
let newPromise = fetch_ticket_lifetime();
|
||||
|
@ -88,61 +88,6 @@ PASS_ERROR = gettext('Error: {error}\n {pass_error}').format(
|
||||
class BrowserModule(PgAdminModule):
|
||||
LABEL = gettext('Browser')
|
||||
|
||||
def get_own_stylesheets(self):
|
||||
stylesheets = []
|
||||
context_menu_file = 'vendor/jQuery-contextMenu/' \
|
||||
'jquery.contextMenu.min.css'
|
||||
wcdocker_file = 'vendor/wcDocker/wcDocker.min.css'
|
||||
if current_app.debug:
|
||||
context_menu_file = 'vendor/jQuery-contextMenu/' \
|
||||
'jquery.contextMenu.css'
|
||||
wcdocker_file = 'vendor/wcDocker/wcDocker.css'
|
||||
# Add browser stylesheets
|
||||
for (endpoint, filename) in [
|
||||
('static', 'vendor/codemirror/codemirror.css'),
|
||||
('static', 'vendor/codemirror/addon/dialog/dialog.css'),
|
||||
('static', context_menu_file),
|
||||
('static', wcdocker_file)
|
||||
]:
|
||||
stylesheets.append(url_for(endpoint, filename=filename))
|
||||
return stylesheets
|
||||
|
||||
def get_own_menuitems(self):
|
||||
menus = {
|
||||
'file_items': [
|
||||
MenuItem(
|
||||
name='mnu_locklayout',
|
||||
module=PGADMIN_BROWSER,
|
||||
label=gettext('Lock Layout'),
|
||||
priority=999,
|
||||
menu_items=[MenuItem(
|
||||
name='mnu_lock_none',
|
||||
module=PGADMIN_BROWSER,
|
||||
callback='mnu_lock_none',
|
||||
priority=0,
|
||||
label=gettext('None'),
|
||||
checked=True
|
||||
), MenuItem(
|
||||
name='mnu_lock_docking',
|
||||
module=PGADMIN_BROWSER,
|
||||
callback='mnu_lock_docking',
|
||||
priority=1,
|
||||
label=gettext('Prevent Docking'),
|
||||
checked=False
|
||||
), MenuItem(
|
||||
name='mnu_lock_full',
|
||||
module=PGADMIN_BROWSER,
|
||||
callback='mnu_lock_full',
|
||||
priority=2,
|
||||
label=gettext('Full Lock'),
|
||||
checked=False
|
||||
)]
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
return menus
|
||||
|
||||
def register_preferences(self):
|
||||
register_browser_preferences(self)
|
||||
|
||||
@ -156,7 +101,6 @@ class BrowserModule(PgAdminModule):
|
||||
'browser.check_master_password',
|
||||
'browser.set_master_password',
|
||||
'browser.reset_master_password',
|
||||
'browser.lock_layout',
|
||||
]
|
||||
|
||||
def register(self, app, options):
|
||||
@ -416,47 +360,6 @@ def _get_supported_browser():
|
||||
return browser_name, browser_known, version
|
||||
|
||||
|
||||
def check_browser_upgrade():
|
||||
"""
|
||||
This function is used to check the browser version.
|
||||
:return:
|
||||
"""
|
||||
data = None
|
||||
url = '%s?version=%s' % (config.UPGRADE_CHECK_URL, config.APP_VERSION)
|
||||
current_app.logger.debug('Checking version data at: %s' % url)
|
||||
|
||||
try:
|
||||
# Do not wait for more than 5 seconds.
|
||||
# It stuck on rendering the browser.html, while working in the
|
||||
# broken network.
|
||||
if os.path.exists(config.CA_FILE):
|
||||
response = urlopen(url, data, 5, cafile=config.CA_FILE)
|
||||
else:
|
||||
response = urlopen(url, data, 5)
|
||||
current_app.logger.debug(
|
||||
'Version check HTTP response code: %d' % response.getcode()
|
||||
)
|
||||
|
||||
if response.getcode() == 200:
|
||||
data = json.loads(response.read().decode('utf-8'))
|
||||
current_app.logger.debug('Response data: %s' % data)
|
||||
except Exception:
|
||||
current_app.logger.exception('Exception when checking for update')
|
||||
|
||||
if data is not None and \
|
||||
data[config.UPGRADE_CHECK_KEY]['version_int'] > \
|
||||
config.APP_VERSION_INT:
|
||||
msg = render_template(
|
||||
MODULE_NAME + "/upgrade.html",
|
||||
current_version=config.APP_VERSION,
|
||||
upgrade_version=data[config.UPGRADE_CHECK_KEY]['version'],
|
||||
product_name=config.APP_NAME,
|
||||
download_url=data[config.UPGRADE_CHECK_KEY]['download_url']
|
||||
)
|
||||
|
||||
flash(msg, MessageType.WARNING)
|
||||
|
||||
|
||||
@blueprint.route("/")
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
@ -491,15 +394,6 @@ def index():
|
||||
|
||||
flash(msg, MessageType.WARNING)
|
||||
|
||||
# Get the current version info from the website, and flash a message if
|
||||
# the user is out of date, and the check is enabled.
|
||||
if config.UPGRADE_CHECK_ENABLED:
|
||||
last_check = get_setting('LastUpdateCheck', default='0')
|
||||
today = time.strftime('%Y%m%d')
|
||||
if int(last_check) < int(today):
|
||||
check_browser_upgrade()
|
||||
store_setting('LastUpdateCheck', today)
|
||||
|
||||
session['allow_save_password'] = True
|
||||
|
||||
if config.SERVER_MODE and not config.MASTER_PASSWORD_REQUIRED and \
|
||||
@ -605,7 +499,6 @@ def utils():
|
||||
editor_indent_with_tabs = False if editor_use_spaces else True
|
||||
|
||||
prefs = Preferences.module('browser')
|
||||
current_ui_lock = prefs.preference('lock_layout').get()
|
||||
# Try to fetch current libpq version from the driver
|
||||
try:
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
@ -672,7 +565,6 @@ def utils():
|
||||
auth_source=auth_source,
|
||||
heartbeat_timeout=config.SERVER_HEARTBEAT_TIMEOUT,
|
||||
password_length_min=config.PASSWORD_LENGTH_MIN,
|
||||
current_ui_lock=current_ui_lock,
|
||||
shared_storage_list=shared_storage_list,
|
||||
restricted_shared_storage_list=[] if current_user.has_role(
|
||||
"Administrator") else restricted_shared_storage_list,
|
||||
@ -689,19 +581,6 @@ def exposed_urls():
|
||||
)
|
||||
|
||||
|
||||
@blueprint.route("/js/constants.js")
|
||||
@pgCSRFProtect.exempt
|
||||
def app_constants():
|
||||
return make_response(
|
||||
render_template('browser/js/constants.js',
|
||||
INTERNAL=INTERNAL,
|
||||
LDAP=LDAP,
|
||||
KERBEROS=KERBEROS,
|
||||
OAUTH2=OAUTH2),
|
||||
200, {'Content-Type': MIMETYPE_APP_JS}
|
||||
)
|
||||
|
||||
|
||||
@blueprint.route("/js/error.js")
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
@ -1033,21 +912,6 @@ def set_master_password():
|
||||
)
|
||||
|
||||
|
||||
@blueprint.route("/lock_layout", endpoint="lock_layout", methods=["PUT"])
|
||||
def lock_layout():
|
||||
data = None
|
||||
|
||||
if hasattr(request.data, 'decode'):
|
||||
data = request.data.decode('utf-8')
|
||||
|
||||
if data != '':
|
||||
data = json.loads(data)
|
||||
|
||||
blueprint.lock_layout.set(data['value'])
|
||||
|
||||
return make_json_response()
|
||||
|
||||
|
||||
# Only register route if SECURITY_CHANGEABLE is set to True
|
||||
# We can't access app context here so cannot
|
||||
# use app.config['SECURITY_CHANGEABLE']
|
||||
|
@ -15,12 +15,6 @@ from pgadmin.utils.constants import PREF_LABEL_DISPLAY,\
|
||||
from flask import current_app
|
||||
import config
|
||||
|
||||
LOCK_LAYOUT_LEVEL = {
|
||||
'PREVENT_DOCKING': 'docking',
|
||||
'FULL': 'full',
|
||||
'NONE': 'none'
|
||||
}
|
||||
|
||||
|
||||
def register_browser_preferences(self):
|
||||
self.show_system_objects = self.preference.register(
|
||||
@ -97,21 +91,6 @@ def register_browser_preferences(self):
|
||||
)
|
||||
)
|
||||
|
||||
self.lock_layout = self.preference.register(
|
||||
'display', 'lock_layout',
|
||||
gettext('Lock Layout'), 'radioModern', LOCK_LAYOUT_LEVEL['NONE'],
|
||||
category_label=PREF_LABEL_DISPLAY, options=[
|
||||
{'label': gettext('None'), 'value': LOCK_LAYOUT_LEVEL['NONE']},
|
||||
{'label': gettext('Prevent Docking'),
|
||||
'value': LOCK_LAYOUT_LEVEL['PREVENT_DOCKING']},
|
||||
{'label': gettext('Full Lock'),
|
||||
'value': LOCK_LAYOUT_LEVEL['FULL']},
|
||||
],
|
||||
help_str=gettext(
|
||||
'Lock the UI layout at different levels'
|
||||
)
|
||||
)
|
||||
|
||||
self.table_row_count_threshold = self.preference.register(
|
||||
'properties', 'table_row_count_threshold',
|
||||
gettext("Count rows if estimated less than"), 'integer', 2000,
|
||||
|
@ -370,9 +370,6 @@ def convert_length_precision_to_string(data):
|
||||
:return:
|
||||
"""
|
||||
|
||||
# We need to handle the below case because jquery has changed
|
||||
# undefined/null values to empty strings
|
||||
# https://github.com/jquery/jquery/commit/36d2d9ae937f626d98319ed850905e8d1cbfd078
|
||||
if 'attlen' in data and data['attlen'] == '':
|
||||
data['attlen'] = None
|
||||
elif 'attlen' in data and data['attlen'] is not None:
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
import { getNodeListByName } from '../../../../../../../../static/js/node_ajax';
|
||||
import CompoundTriggerSchema from './compound_trigger.ui';
|
||||
import Notify from '../../../../../../../../../static/js/helpers/Notifier';
|
||||
import getApiInstance from '../../../../../../../../../static/js/api_instance';
|
||||
|
||||
define('pgadmin.node.compound_trigger', [
|
||||
@ -118,14 +117,14 @@ define('pgadmin.node.compound_trigger', [
|
||||
{'is_enable_trigger' : 'O'}
|
||||
).then(({data: res})=> {
|
||||
if(res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-compound_trigger';
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
t.updateAndReselectNode(i, data);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
},
|
||||
@ -146,14 +145,14 @@ define('pgadmin.node.compound_trigger', [
|
||||
{'is_enable_trigger' : 'D'}
|
||||
).then(({data: res})=> {
|
||||
if(res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-compound_trigger-bad';
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
t.updateAndReselectNode(i, data);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
},
|
||||
|
@ -8,7 +8,6 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import CheckConstraintSchema from './check_constraint.ui';
|
||||
import Notify from '../../../../../../../../../../static/js/helpers/Notifier';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance from '../../../../../../../../../../static/js/api_instance';
|
||||
|
||||
@ -73,7 +72,7 @@ define('pgadmin.node.check_constraint', [
|
||||
getApiInstance().get(obj.generate_url(i, 'validate', d, true))
|
||||
.then(({data: res})=>{
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.valid = true;
|
||||
data.icon = 'icon-check_constraint';
|
||||
@ -83,7 +82,7 @@ define('pgadmin.node.check_constraint', [
|
||||
}
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import { SCHEMA_STATE_ACTIONS } from '../../../../../../../../../../static/js/Sc
|
||||
import DataGridViewWithHeaderForm from '../../../../../../../../../../static/js/helpers/DataGridViewWithHeaderForm';
|
||||
import { getNodeAjaxOptions, getNodeListByName } from '../../../../../../../../../static/js/node_ajax';
|
||||
import TableSchema from '../../../../static/js/table.ui';
|
||||
import Notify from '../../../../../../../../../../static/js/helpers/Notifier';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
function getData(data) {
|
||||
let res = [];
|
||||
@ -275,7 +275,7 @@ export default class ExclusionConstraintSchema extends BaseUISchema {
|
||||
options: this.fieldOptions.amname,
|
||||
deferredDepChange: (state, source, topState, actionObj)=>{
|
||||
return new Promise((resolve)=>{
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Change access method?'),
|
||||
gettext('Changing access method will clear columns collection'),
|
||||
function () {
|
||||
|
@ -8,7 +8,6 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { getNodeForeignKeySchema } from './foreign_key.ui';
|
||||
import Notify from '../../../../../../../../../../static/js/helpers/Notifier';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance from '../../../../../../../../../../static/js/api_instance';
|
||||
|
||||
@ -69,7 +68,7 @@ define('pgadmin.node.foreign_key', [
|
||||
getApiInstance().get(obj.generate_url(i, 'validate', d, true))
|
||||
.then(({data: res})=>{
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.valid = true;
|
||||
data.icon = 'icon-foreign_key';
|
||||
@ -79,7 +78,7 @@ define('pgadmin.node.foreign_key', [
|
||||
}
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import DataGridViewWithHeaderForm from '../../../../../../../../../static/js/helpers/DataGridViewWithHeaderForm';
|
||||
import _ from 'lodash';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import Notify from '../../../../../../../../../static/js/helpers/Notifier';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
|
||||
function inSchema(node_info) {
|
||||
@ -476,7 +476,7 @@ export default class IndexSchema extends BaseUISchema {
|
||||
};
|
||||
if((state.amname != actionObj?.oldState.amname) && state.columns?.length > 0) {
|
||||
return new Promise((resolve)=>{
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Warning'),
|
||||
gettext('Changing access method will clear columns collection. Do you want to continue?'),
|
||||
function () {
|
||||
|
@ -8,18 +8,17 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { getNodePartitionTableSchema } from './partition.ui';
|
||||
import Notify from '../../../../../../../../../static/js/helpers/Notifier';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance from '../../../../../../../../../static/js/api_instance';
|
||||
|
||||
define([
|
||||
'sources/gettext', 'sources/url_for', 'jquery',
|
||||
'sources/gettext', 'sources/url_for',
|
||||
'sources/pgadmin', 'pgadmin.browser',
|
||||
'pgadmin.node.schema.dir/schema_child_tree_node', 'sources/utils',
|
||||
'pgadmin.browser.collection',
|
||||
],
|
||||
function(
|
||||
gettext, url_for, $, pgAdmin, pgBrowser,
|
||||
gettext, url_for, pgAdmin, pgBrowser,
|
||||
SchemaChildTreeNode, pgadminUtils
|
||||
) {
|
||||
|
||||
@ -151,7 +150,7 @@ function(
|
||||
},
|
||||
on_done: function(res, data, t, i) {
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-partition';
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
@ -186,12 +185,12 @@ function(
|
||||
getApiInstance().put(obj.generate_url(i, 'set_trigger' , d, true), params)
|
||||
.then(({data: res})=>{
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.updateAndReselectNode(i, d);
|
||||
}
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
},
|
||||
@ -215,7 +214,7 @@ function(
|
||||
if (!d)
|
||||
return false;
|
||||
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Truncate Table'),
|
||||
gettext('Are you sure you want to truncate table %s?', d.label),
|
||||
function () {
|
||||
@ -225,7 +224,7 @@ function(
|
||||
obj.on_done(res, data, t, i);
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
},
|
||||
@ -241,7 +240,7 @@ function(
|
||||
if (!d)
|
||||
return false;
|
||||
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Reset statistics'),
|
||||
gettext('Are you sure you want to reset the statistics for table "%s"?', d._label),
|
||||
function () {
|
||||
@ -251,7 +250,7 @@ function(
|
||||
obj.on_done(res, data, t, i);
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
},
|
||||
@ -276,14 +275,14 @@ function(
|
||||
title = gettext('Detach Partition Finalize');
|
||||
}
|
||||
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
title,
|
||||
gettext('Are you sure you want to detach the partition %s?', d._label),
|
||||
function () {
|
||||
getApiInstance().put(obj.generate_url(i, 'detach' , d, true), params)
|
||||
.then(({data: res})=>{
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
let n = t.next(i);
|
||||
if (!n) {
|
||||
n = t.prev(i);
|
||||
@ -298,7 +297,7 @@ function(
|
||||
}
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
});
|
||||
},
|
||||
function() {/*This is intentional (SonarQube)*/}
|
||||
|
@ -7,15 +7,14 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import RuleSchema from './rule.ui';
|
||||
import Notify from '../../../../../../../../../static/js/helpers/Notifier';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance from '../../../../../../../../../static/js/api_instance';
|
||||
|
||||
define('pgadmin.node.rule', [
|
||||
'sources/gettext', 'sources/url_for', 'jquery',
|
||||
'sources/gettext', 'sources/url_for',
|
||||
'sources/pgadmin', 'pgadmin.browser',
|
||||
'pgadmin.node.schema.dir/schema_child_tree_node',
|
||||
], function(gettext, url_for, $, pgAdmin, pgBrowser, SchemaChildTreeNode) {
|
||||
], function(gettext, url_for, pgAdmin, pgBrowser, SchemaChildTreeNode) {
|
||||
|
||||
/**
|
||||
Create and add a rule collection into nodes
|
||||
@ -137,14 +136,14 @@ define('pgadmin.node.rule', [
|
||||
let data = d;
|
||||
getApiInstance().put(obj.generate_url(i, 'obj' , d, true), {'is_enable_rule' : 'O'})
|
||||
.then(()=>{
|
||||
Notify.success('Rule updated.');
|
||||
pgAdmin.Browser.notifier.success('Rule updated.');
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-rule';
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
t.updateAndReselectNode(i, data);
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
},
|
||||
@ -162,14 +161,14 @@ define('pgadmin.node.rule', [
|
||||
let data = d;
|
||||
getApiInstance().put(obj.generate_url(i, 'obj' , d, true), {'is_enable_rule' : 'D'})
|
||||
.then(()=>{
|
||||
Notify.success('Rule updated');
|
||||
pgAdmin.Browser.notifier.success('Rule updated');
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-rule-bad';
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
t.updateAndReselectNode(i, data);
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
},
|
||||
|
@ -8,15 +8,16 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import axios from 'axios';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
export function disableTriggers(tree, Notify, generateUrl, args) {
|
||||
return setTriggers(tree, Notify, generateUrl, args, {is_enable_trigger: 'D' });
|
||||
export function disableTriggers(tree, generateUrl, args) {
|
||||
return setTriggers(tree, generateUrl, args, {is_enable_trigger: 'D' });
|
||||
}
|
||||
export function enableTriggers(tree, Notify, generateUrl, args) {
|
||||
return setTriggers(tree, Notify, generateUrl, args, {is_enable_trigger: 'O' });
|
||||
export function enableTriggers(tree, generateUrl, args) {
|
||||
return setTriggers(tree, generateUrl, args, {is_enable_trigger: 'O' });
|
||||
}
|
||||
|
||||
function setTriggers(tree, Notify, generateUrl, args, params) {
|
||||
function setTriggers(tree, generateUrl, args, params) {
|
||||
const treeNode = retrieveTreeNode(args, tree);
|
||||
|
||||
if (!treeNode || treeNode.getData() === null || treeNode.getData() === undefined)
|
||||
@ -28,7 +29,7 @@ function setTriggers(tree, Notify, generateUrl, args, params) {
|
||||
)
|
||||
.then((res) => {
|
||||
if (res.data.success === 1) {
|
||||
Notify.success(res.data.info);
|
||||
pgAdmin.Browser.notifier.success(res.data.info);
|
||||
treeNode.data.has_enable_triggers = res.data.data.has_enable_triggers;
|
||||
treeNode.reload(tree);
|
||||
|
||||
@ -38,7 +39,7 @@ function setTriggers(tree, Notify, generateUrl, args, params) {
|
||||
try {
|
||||
const err = xhr.response.data;
|
||||
if (err.success === 0) {
|
||||
Notify.error(err.errormsg);
|
||||
pgAdmin.Browser.notifier.error(err.errormsg);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e.stack || e);
|
||||
|
@ -7,20 +7,19 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { getNodeTableSchema } from './table.ui';
|
||||
import Notify from '../../../../../../../../static/js/helpers/Notifier';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance from '../../../../../../../../static/js/api_instance';
|
||||
|
||||
define('pgadmin.node.table', [
|
||||
'pgadmin.tables.js/enable_disable_triggers',
|
||||
'sources/gettext', 'sources/url_for', 'jquery',
|
||||
'sources/gettext', 'sources/url_for',
|
||||
'sources/pgadmin', 'pgadmin.browser',
|
||||
'pgadmin.node.schema.dir/child','pgadmin.node.schema.dir/schema_child_tree_node',
|
||||
'pgadmin.browser.collection', 'pgadmin.node.column',
|
||||
'pgadmin.node.constraints',
|
||||
], function(
|
||||
tableFunctions,
|
||||
gettext, url_for, $, pgAdmin, pgBrowser, SchemaChild, SchemaChildTreeNode
|
||||
gettext, url_for, pgAdmin, pgBrowser, SchemaChild, SchemaChildTreeNode
|
||||
) {
|
||||
|
||||
if (!pgBrowser.Nodes['coll-table']) {
|
||||
@ -145,7 +144,6 @@ define('pgadmin.node.table', [
|
||||
enable_triggers_on_table: function(args) {
|
||||
tableFunctions.enableTriggers(
|
||||
pgBrowser.tree,
|
||||
Notify,
|
||||
this.generate_url.bind(this),
|
||||
args
|
||||
);
|
||||
@ -154,7 +152,6 @@ define('pgadmin.node.table', [
|
||||
disable_triggers_on_table: function(args) {
|
||||
tableFunctions.disableTriggers(
|
||||
pgBrowser.tree,
|
||||
Notify,
|
||||
this.generate_url.bind(this),
|
||||
args
|
||||
);
|
||||
@ -183,7 +180,7 @@ define('pgadmin.node.table', [
|
||||
if (!d)
|
||||
return false;
|
||||
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Truncate Table'),
|
||||
gettext('Are you sure you want to truncate table %s?', d.label),
|
||||
function () {
|
||||
@ -191,18 +188,18 @@ define('pgadmin.node.table', [
|
||||
getApiInstance().put(obj.generate_url(i, 'truncate' , d, true), params)
|
||||
.then(({data: res})=>{
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = data.is_partitioned ? 'icon-partition': 'icon-table';
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
t.updateAndReselectNode(i, data);
|
||||
}
|
||||
if (res.success == 2) {
|
||||
Notify.error(res.info);
|
||||
pgAdmin.Browser.notifier.error(res.info);
|
||||
}
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
}, function() {/*This is intentional (SonarQube)*/}
|
||||
@ -218,7 +215,7 @@ define('pgadmin.node.table', [
|
||||
if (!d)
|
||||
return false;
|
||||
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Reset statistics'),
|
||||
gettext('Are you sure you want to reset the statistics for table "%s"?', d._label),
|
||||
function () {
|
||||
@ -226,7 +223,7 @@ define('pgadmin.node.table', [
|
||||
getApiInstance().delete(obj.generate_url(i, 'reset' , d, true))
|
||||
.then(({data: res})=>{
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = data.is_partitioned ? 'icon-partition': 'icon-table';
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
@ -234,7 +231,7 @@ define('pgadmin.node.table', [
|
||||
}
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
},
|
||||
@ -257,12 +254,12 @@ define('pgadmin.node.table', [
|
||||
// Fetch the total rows of a table
|
||||
getApiInstance().get(obj.generate_url(i, 'count_rows' , newD, true))
|
||||
.then(({data: res})=>{
|
||||
Notify.success(res.info, null);
|
||||
pgAdmin.Browser.notifier.success(res.info, null);
|
||||
d.rows_cnt = res.data.total_rows;
|
||||
t.updateAndReselectNode(i, d);
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
},
|
||||
|
@ -22,7 +22,7 @@ import { getNodeVacuumSettingsSchema } from '../../../../../static/js/vacuum.ui'
|
||||
import { getNodeForeignKeySchema } from '../../constraints/foreign_key/static/js/foreign_key.ui';
|
||||
import { getNodeExclusionConstraintSchema } from '../../constraints/exclusion_constraint/static/js/exclusion_constraint.ui';
|
||||
import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui';
|
||||
import Notify from '../../../../../../../../static/js/helpers/Notifier';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
export function getNodeTableSchema(treeNodeInfo, itemNodeData, pgBrowser) {
|
||||
const spcname = ()=>getNodeListByName('tablespace', treeNodeInfo, itemNodeData, {}, (m)=>{
|
||||
@ -622,7 +622,7 @@ export default class TableSchema extends BaseUISchema {
|
||||
group: 'advanced', min_version: 90600,
|
||||
depChange: (state)=>{
|
||||
if (state.rlspolicy && this.origData.rlspolicy != state.rlspolicy) {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Check Policy?'),
|
||||
gettext('Please check if any policy exists. If no policy exists for the table, a default-deny policy is used, meaning that no rows are visible or can be modified by other users')
|
||||
);
|
||||
@ -752,7 +752,7 @@ export default class TableSchema extends BaseUISchema {
|
||||
};
|
||||
if(!isEmptyString(state.typname) && isEmptyString(actionObj.oldState.typname)) {
|
||||
return new Promise((resolve)=>{
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Remove column definitions?'),
|
||||
gettext('Changing \'Of type\' will remove column definitions.'),
|
||||
function () {
|
||||
|
@ -8,17 +8,16 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { getNodeListByName, getNodeAjaxOptions } from '../../../../../../../../static/js/node_ajax';
|
||||
import TriggerSchema from './trigger.ui';
|
||||
import Notify from '../../../../../../../../../static/js/helpers/Notifier';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance from '../../../../../../../../../static/js/api_instance';
|
||||
|
||||
define('pgadmin.node.trigger', [
|
||||
'sources/gettext', 'sources/url_for', 'jquery',
|
||||
'sources/gettext', 'sources/url_for',
|
||||
'sources/pgadmin', 'pgadmin.browser',
|
||||
'pgadmin.node.schema.dir/schema_child_tree_node',
|
||||
'pgadmin.browser.collection',
|
||||
], function(
|
||||
gettext, url_for, $, pgAdmin, pgBrowser, SchemaChildTreeNode
|
||||
gettext, url_for, pgAdmin, pgBrowser, SchemaChildTreeNode
|
||||
) {
|
||||
|
||||
if (!pgBrowser.Nodes['coll-trigger']) {
|
||||
@ -118,7 +117,7 @@ define('pgadmin.node.trigger', [
|
||||
getApiInstance().put(obj.generate_url(i, 'enable' , d, true), {'is_enable_trigger' : 'O'})
|
||||
.then(({data: res})=>{
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-trigger';
|
||||
data.has_enable_triggers = res.data.has_enable_triggers;
|
||||
@ -127,7 +126,7 @@ define('pgadmin.node.trigger', [
|
||||
}
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
},
|
||||
@ -146,7 +145,7 @@ define('pgadmin.node.trigger', [
|
||||
getApiInstance().put(obj.generate_url(i, 'enable' , d, true), {'is_enable_trigger' : 'D'})
|
||||
.then(({data: res})=>{
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-trigger-bad';
|
||||
data.has_enable_triggers = res.data.has_enable_triggers;
|
||||
@ -155,7 +154,7 @@ define('pgadmin.node.trigger', [
|
||||
}
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
},
|
||||
|
@ -11,17 +11,16 @@ import MViewSchema from './mview.ui';
|
||||
import { getNodeListByName, getNodeAjaxOptions } from '../../../../../../../static/js/node_ajax';
|
||||
import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui';
|
||||
import { getNodeVacuumSettingsSchema } from '../../../../../static/js/vacuum.ui';
|
||||
import Notify from '../../../../../../../../static/js/helpers/Notifier';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance from '../../../../../../../../static/js/api_instance';
|
||||
|
||||
define('pgadmin.node.mview', [
|
||||
'sources/gettext', 'sources/url_for', 'jquery',
|
||||
'sources/gettext', 'sources/url_for',
|
||||
'sources/pgadmin', 'pgadmin.browser',
|
||||
'pgadmin.node.schema.dir/child',
|
||||
'pgadmin.node.schema.dir/schema_child_tree_node', 'sources/utils',
|
||||
], function(
|
||||
gettext, url_for, $, pgAdmin, pgBrowser,
|
||||
gettext, url_for, pgAdmin, pgBrowser,
|
||||
schemaChild, schemaChildTreeNode, commonUtils
|
||||
) {
|
||||
|
||||
@ -176,7 +175,7 @@ define('pgadmin.node.mview', [
|
||||
if (pgBrowser.tree.hasParent(j)) {
|
||||
j = pgBrowser.tree.parent(j);
|
||||
} else {
|
||||
Notify.alert(gettext('Please select server or child node from tree.'));
|
||||
pgAdmin.Browser.notifier.alert(gettext('Please select server or child node from tree.'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -193,7 +192,7 @@ define('pgadmin.node.mview', [
|
||||
api.get(obj.generate_url(i, 'check_utility_exists' , d, true))
|
||||
.then(({data: res})=>{
|
||||
if (!res.success) {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Utility not found'),
|
||||
res.errormsg
|
||||
);
|
||||
@ -206,20 +205,20 @@ define('pgadmin.node.mview', [
|
||||
//Do nothing as we are creating the job and exiting from the main dialog
|
||||
pgBrowser.BgProcessManager.startProcess(refreshed_res.data.job_id, refreshed_res.data.desc);
|
||||
} else {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Failed to create materialized view refresh job.'),
|
||||
refreshed_res.errormsg
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error)=>{
|
||||
Notify.pgRespErrorNotify(
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(
|
||||
error, gettext('Failed to create materialized view refresh job.')
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch(()=>{
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Utility not found'),
|
||||
gettext('Failed to fetch Utility information')
|
||||
);
|
||||
|
@ -11,16 +11,15 @@ import { getNodeAjaxOptions, getNodeListByName } from '../../../../../static/js/
|
||||
import { getNodePrivilegeRoleSchema } from '../../../static/js/privilege.ui';
|
||||
import { getNodeVariableSchema } from '../../../static/js/variable.ui';
|
||||
import DatabaseSchema from './database.ui';
|
||||
import Notify from '../../../../../../static/js/helpers/Notifier';
|
||||
import { showServerPassword } from '../../../../../../static/js/Dialogs/index';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance, { parseApiError } from '../../../../../../static/js/api_instance';
|
||||
|
||||
define('pgadmin.node.database', [
|
||||
'sources/gettext', 'sources/url_for', 'jquery',
|
||||
'sources/gettext', 'sources/url_for',
|
||||
'sources/pgadmin', 'pgadmin.browser.utils',
|
||||
'pgadmin.authenticate.kerberos', 'pgadmin.browser.collection',
|
||||
], function(gettext, url_for, $, pgAdmin, pgBrowser, Kerberos) {
|
||||
], function(gettext, url_for, pgAdmin, pgBrowser, Kerberos) {
|
||||
|
||||
function canDeleteWithForce(itemNodeData, item) {
|
||||
let treeData = pgBrowser.tree.getTreeNodeHierarchy(item),
|
||||
@ -189,7 +188,7 @@ define('pgadmin.node.database', [
|
||||
connect(self, d, t, i, true);
|
||||
return;
|
||||
}
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Connection lost'),
|
||||
gettext('Would you like to reconnect to the database?'),
|
||||
function() {
|
||||
@ -231,7 +230,7 @@ define('pgadmin.node.database', [
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
|
||||
if (d) {
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Disconnect from database'),
|
||||
gettext('Are you sure you want to disconnect from database - %s?', d.label),
|
||||
function() {
|
||||
@ -244,7 +243,7 @@ define('pgadmin.node.database', [
|
||||
if(res.data.info_prefix) {
|
||||
res.info = `${_.escape(res.data.info_prefix)} - ${res.info}`;
|
||||
}
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.connected = false;
|
||||
data.icon = data.isTemplate ? 'icon-database-template-not-connected':'icon-database-not-connected';
|
||||
@ -258,14 +257,14 @@ define('pgadmin.node.database', [
|
||||
|
||||
} else {
|
||||
try {
|
||||
Notify.error(res.errormsg);
|
||||
pgAdmin.Browser.notifier.error(res.errormsg);
|
||||
} catch (e) {
|
||||
console.warn(e.stack || e);
|
||||
}
|
||||
t.unload(i);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
},
|
||||
@ -408,7 +407,7 @@ define('pgadmin.node.database', [
|
||||
tree.setInode(_item);
|
||||
let dbIcon = data.isTemplate ? 'icon-database-template-not-connected':'icon-database-not-connected';
|
||||
tree.addIcon(_item, {icon: dbIcon});
|
||||
Notify.pgNotifier(fun_error, error, gettext('Connect to database.'));
|
||||
pgAdmin.Browser.notifier.pgNotifier(fun_error, error, gettext('Connect to database.'));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
@ -418,7 +417,7 @@ define('pgadmin.node.database', [
|
||||
tree.addIcon(_item, {icon: dbIcon});
|
||||
}
|
||||
|
||||
Notify.pgNotifier('error', error, 'Error', function(msg) {
|
||||
pgAdmin.Browser.notifier.pgNotifier('error', error, 'Error', function(msg) {
|
||||
setTimeout(function() {
|
||||
if (msg == 'CRYPTKEY_SET') {
|
||||
connect_to_database(_model, _data, _tree, _item, _wasConnected);
|
||||
@ -454,9 +453,9 @@ define('pgadmin.node.database', [
|
||||
res.info = `${_.escape(res.data.info_prefix)} - ${res.info}`;
|
||||
}
|
||||
if(res.data.already_connected) {
|
||||
Notify.info(res.info);
|
||||
pgAdmin.Browser.notifier.info(res.info);
|
||||
} else {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
}
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin:database:connected', _item, _data
|
||||
|
@ -10,7 +10,7 @@ import { getNodeListByName } from '../../../../../../static/js/node_ajax';
|
||||
import SubscriptionSchema from './subscription.ui';
|
||||
import getApiInstance from '../../../../../../../static/js/api_instance';
|
||||
import _ from 'lodash';
|
||||
import Notify from '../../../../../../../static/js/helpers/Notifier';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
define('pgadmin.node.subscription', [
|
||||
'sources/gettext', 'sources/url_for',
|
||||
@ -100,12 +100,12 @@ define('pgadmin.node.subscription', [
|
||||
.then(res=>{
|
||||
if ((res.data.errormsg === '') && !_.isNull(res.data.data)){
|
||||
resolve(res.data.data);
|
||||
Notify.info(
|
||||
pgAdmin.Browser.notifier.info(
|
||||
gettext('Publication fetched successfully.')
|
||||
);
|
||||
}else if(!_.isNull(res.data.errormsg) && _.isNull(res.data.data)){
|
||||
reject(res.data.errormsg);
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Check connection?'),
|
||||
gettext(res.data.errormsg)
|
||||
);
|
||||
|
@ -10,13 +10,13 @@
|
||||
import { getNodeAjaxOptions } from '../../../../../static/js/node_ajax';
|
||||
import PgaJobSchema from './pga_job.ui';
|
||||
import { getNodePgaJobStepSchema } from '../../steps/static/js/pga_jobstep.ui';
|
||||
import Notify from '../../../../../../static/js/helpers/Notifier';
|
||||
import getApiInstance from '../../../../../../static/js/api_instance';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
define('pgadmin.node.pga_job', [
|
||||
'sources/gettext', 'sources/url_for', 'jquery', 'pgadmin.browser',
|
||||
'sources/gettext', 'sources/url_for', 'pgadmin.browser',
|
||||
'pgadmin.node.pga_jobstep', 'pgadmin.node.pga_schedule',
|
||||
], function(gettext, url_for, $, pgBrowser) {
|
||||
], function(gettext, url_for, pgBrowser) {
|
||||
|
||||
if (!pgBrowser.Nodes['coll-pga_job']) {
|
||||
pgBrowser.Nodes['coll-pga_job'] =
|
||||
@ -95,10 +95,10 @@ define('pgadmin.node.pga_job', [
|
||||
getApiInstance().put(
|
||||
obj.generate_url(i, 'run_now', d, true),
|
||||
).then(({data: res})=> {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.unload(i);
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
}
|
||||
|
@ -12,8 +12,6 @@ import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import url_for from 'sources/url_for';
|
||||
import { getNodeListByName, generateNodeUrl } from '../../../../../static/js/node_ajax';
|
||||
import pgBrowser from 'top/browser/static/js/browser';
|
||||
import { getUtilityView } from '../../../../../static/js/utility_view';
|
||||
import Notify from '../../../../../../static/js/helpers/Notifier';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
@ -180,17 +178,6 @@ export default class RoleReassign extends BaseUISchema{
|
||||
}
|
||||
}
|
||||
|
||||
function saveCallBack (data) {
|
||||
if (data.errormsg) {
|
||||
Notify.alert(
|
||||
gettext('Error'),
|
||||
gettext(data.errormsg)
|
||||
);
|
||||
} else {
|
||||
Notify.success(gettext(data.info));
|
||||
}
|
||||
}
|
||||
|
||||
function getUISchema(treeNodeInfo, itemNodeData ) {
|
||||
return new RoleReassign(
|
||||
{
|
||||
@ -211,13 +198,7 @@ export function showRoleReassign() {
|
||||
treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item),
|
||||
itemNodeData = pgBrowser.tree.findNodeByDomElement(item).getData();
|
||||
|
||||
pgBrowser.Node.registerUtilityPanel();
|
||||
let panel = pgBrowser.Node.addUtilityPanel(pgBrowser.stdW.md, 480),
|
||||
j = panel.$container.find('.obj_properties').first();
|
||||
panel.title(gettext(`Reassign/Drop Owned - ${data.label}`));
|
||||
panel.focus();
|
||||
|
||||
const baseUrl = generateNodeUrl.call( pgAdmin.Browser.Nodes[data._type], treeNodeInfo, 'reassign', data, true);
|
||||
const urlBase = generateNodeUrl.call( pgAdmin.Browser.Nodes[data._type], treeNodeInfo, 'reassign', data, true);
|
||||
|
||||
let schema = getUISchema(treeNodeInfo, itemNodeData),
|
||||
sqlHelpUrl = '',
|
||||
@ -227,6 +208,9 @@ export function showRoleReassign() {
|
||||
'filename': 'role_reassign_dialog.html',
|
||||
});
|
||||
|
||||
getUtilityView(
|
||||
schema, treeNodeInfo, 'create', 'dialog', j[0], panel, saveCallBack, extraData, 'Reassign/Drop', baseUrl, sqlHelpUrl, helpUrl);
|
||||
}
|
||||
pgAdmin.Browser.Events.trigger('pgadmin:utility:show', item,
|
||||
gettext(gettext(`Reassign/Drop Owned - ${data.label}`), treeNodeInfo.table.label),{
|
||||
schema, extraData, urlBase, sqlHelpUrl, helpUrl, saveBtnName: gettext('Reassign/Drop'),
|
||||
}, pgAdmin.Browser.stdW.md
|
||||
);
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import _ from 'lodash';
|
||||
import url_for from 'sources/url_for';
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import getApiInstance from '../../../../../static/js/api_instance';
|
||||
import Notify from '../../../../../static/js/helpers/Notifier';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
export function getBinaryPathSchema() {
|
||||
@ -60,15 +59,15 @@ export default class BinaryPathSchema extends BaseUISchema {
|
||||
validate: (data) => {
|
||||
const api = getApiInstance();
|
||||
if (_.isNull(data) || data.trim() === '') {
|
||||
Notify.alert(gettext('Validate Path'), gettext('Path should not be empty.'));
|
||||
pgAdmin.Browser.notifier.alert(gettext('Validate Path'), gettext('Path should not be empty.'));
|
||||
} else {
|
||||
api.post(url_for('misc.validate_binary_path'),
|
||||
JSON.stringify({ 'utility_path': data }))
|
||||
.then(function (res) {
|
||||
Notify.alert(gettext('Validate binary path'), gettext(res.data.data));
|
||||
pgAdmin.Browser.notifier.alert(gettext('Validate binary path'), gettext(res.data.data));
|
||||
})
|
||||
.catch(function (error) {
|
||||
Notify.pgNotifier('error', error, gettext('Failed to validate binary path.'));
|
||||
pgAdmin.Browser.notifier.pgNotifier('error', error, gettext('Failed to validate binary path.'));
|
||||
});
|
||||
}
|
||||
return true;
|
||||
|
@ -9,18 +9,17 @@
|
||||
|
||||
import { getNodeListById } from '../../../../static/js/node_ajax';
|
||||
import ServerSchema from './server.ui';
|
||||
import Notify from '../../../../../static/js/helpers/Notifier';
|
||||
import { showServerPassword, showChangeServerPassword, showNamedRestorePoint } from '../../../../../static/js/Dialogs/index';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance, { parseApiError } from '../../../../../static/js/api_instance';
|
||||
|
||||
define('pgadmin.node.server', [
|
||||
'sources/gettext', 'sources/url_for', 'jquery',
|
||||
'sources/gettext', 'sources/url_for',
|
||||
'sources/pgadmin', 'pgadmin.browser',
|
||||
'pgadmin.user_management.current_user',
|
||||
'pgadmin.authenticate.kerberos',
|
||||
], function(
|
||||
gettext, url_for, $, pgAdmin, pgBrowser,
|
||||
gettext, url_for, pgAdmin, pgBrowser,
|
||||
current_user, Kerberos,
|
||||
) {
|
||||
|
||||
@ -42,6 +41,12 @@ define('pgadmin.node.server', [
|
||||
can_expand: function(d) {
|
||||
return d && d.connected;
|
||||
},
|
||||
title: function(d, action) {
|
||||
if(action == 'create') {
|
||||
return gettext('Register - %s', this.label);
|
||||
}
|
||||
return d.label??'';
|
||||
},
|
||||
Init: function() {
|
||||
/* Avoid multiple registration of same menus */
|
||||
if (this.initialized)
|
||||
@ -201,7 +206,7 @@ define('pgadmin.node.server', [
|
||||
obj.generate_url(i, 'connect', d, true),
|
||||
).then(({data: res})=> {
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info, null);
|
||||
d = t.itemData(i);
|
||||
t.removeIcon(i);
|
||||
d.connected = false;
|
||||
@ -226,7 +231,7 @@ define('pgadmin.node.server', [
|
||||
}
|
||||
else {
|
||||
try {
|
||||
Notify.error(res.errormsg);
|
||||
pgAdmin.Browser.notifier.error(res.errormsg);
|
||||
} catch (e) {
|
||||
console.warn(e.stack || e);
|
||||
}
|
||||
@ -234,13 +239,13 @@ define('pgadmin.node.server', [
|
||||
}
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
};
|
||||
|
||||
if (notify) {
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Disconnect from server'),
|
||||
gettext('Are you sure you want to disconnect from the server %s?', d.label),
|
||||
function() { disconnect(); },
|
||||
@ -293,7 +298,7 @@ define('pgadmin.node.server', [
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
|
||||
if (d) {
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Reload server configuration'),
|
||||
gettext('Are you sure you want to reload the server configuration on %s?', d.label),
|
||||
function() {
|
||||
@ -301,13 +306,13 @@ define('pgadmin.node.server', [
|
||||
obj.generate_url(i, 'reload', d, true),
|
||||
).then(({data: res})=> {
|
||||
if (res.data.status) {
|
||||
Notify.success(res.data.result);
|
||||
pgAdmin.Browser.notifier.success(res.data.result);
|
||||
}
|
||||
else {
|
||||
Notify.error(res.data.result);
|
||||
pgAdmin.Browser.notifier.error(res.data.result);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
},
|
||||
@ -351,7 +356,7 @@ define('pgadmin.node.server', [
|
||||
}
|
||||
showChangeServerPassword(gettext('Change Password'), d, obj, i, is_pgpass_file_used);
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
});
|
||||
}
|
||||
|
||||
@ -360,7 +365,7 @@ define('pgadmin.node.server', [
|
||||
|
||||
on_done: function(res, t, i) {
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.itemData(i).wal_pause=res.data.wal_pause;
|
||||
t.deselect(i);
|
||||
// Fetch updated data from server
|
||||
@ -386,7 +391,7 @@ define('pgadmin.node.server', [
|
||||
).then(({data: res})=> {
|
||||
obj.callbacks.on_done(res, t, i);
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
},
|
||||
@ -407,7 +412,7 @@ define('pgadmin.node.server', [
|
||||
).then(({data: res})=> {
|
||||
obj.callbacks.on_done(res, t, i);
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.unload(i);
|
||||
});
|
||||
},
|
||||
@ -421,7 +426,7 @@ define('pgadmin.node.server', [
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
|
||||
if (d) {
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Clear saved password'),
|
||||
gettext('Are you sure you want to clear the saved password for server %s?', d.label),
|
||||
function() {
|
||||
@ -429,7 +434,7 @@ define('pgadmin.node.server', [
|
||||
obj.generate_url(i, 'clear_saved_password' , d, true)
|
||||
).then(({data: res})=> {
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.itemData(i).is_password_saved=res.data.is_password_saved;
|
||||
t.deselect(i);
|
||||
setTimeout(function() {
|
||||
@ -437,10 +442,10 @@ define('pgadmin.node.server', [
|
||||
});
|
||||
}
|
||||
else {
|
||||
Notify.error(res.info);
|
||||
pgAdmin.Browser.notifier.error(res.info);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
});
|
||||
},
|
||||
function() { return true; }
|
||||
@ -459,7 +464,7 @@ define('pgadmin.node.server', [
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
|
||||
if (d) {
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Clear SSH Tunnel password'),
|
||||
gettext('Are you sure you want to clear the saved password of SSH Tunnel for server %s?', d.label),
|
||||
function() {
|
||||
@ -467,14 +472,14 @@ define('pgadmin.node.server', [
|
||||
obj.generate_url(i, 'clear_sshtunnel_password' , d, true)
|
||||
).then(({data: res})=> {
|
||||
if (res.success == 1) {
|
||||
Notify.success(res.info);
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.itemData(i).is_tunnel_password_saved=res.data.is_tunnel_password_saved;
|
||||
}
|
||||
else {
|
||||
Notify.error(res.info);
|
||||
pgAdmin.Browser.notifier.error(res.info);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
});
|
||||
},
|
||||
function() { return true; }
|
||||
@ -542,7 +547,7 @@ define('pgadmin.node.server', [
|
||||
pgBrowser.Events.on(
|
||||
'pgadmin:server:connect:cancelled', disconnect
|
||||
);
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Connection lost'),
|
||||
gettext('Would you like to reconnect to the database?'),
|
||||
function() {
|
||||
@ -565,12 +570,12 @@ define('pgadmin.node.server', [
|
||||
|
||||
let checkSupportedVersion = function (version, info) {
|
||||
if (!_.isUndefined(version) && !_.isNull(version) && version < 100000) {
|
||||
Notify.warning(gettext('You have connected to a server version that is older ' +
|
||||
pgAdmin.Browser.notifier.warning(gettext('You have connected to a server version that is older ' +
|
||||
'than is supported by pgAdmin. This may cause pgAdmin to break in strange and ' +
|
||||
'unpredictable ways. Or a plague of frogs. Either way, you have been warned!') +
|
||||
'<br /><br />' + gettext('Server connected'), null);
|
||||
} else if (!_.isUndefined(info) && !_.isNull(info)) {
|
||||
Notify.success(info);
|
||||
pgAdmin.Browser.notifier.success(info);
|
||||
}
|
||||
};
|
||||
|
||||
@ -588,7 +593,7 @@ define('pgadmin.node.server', [
|
||||
data.is_connecting = false;
|
||||
tree.unload(item);
|
||||
tree.addIcon(item, {icon: 'icon-shared-server-not-connected'});
|
||||
Notify.info('Please enter the server details to connect to the server. This server is a shared server.');
|
||||
pgAdmin.Browser.notifier.info('Please enter the server details to connect to the server. This server is a shared server.');
|
||||
} else {
|
||||
data.is_connecting = false;
|
||||
tree.unload(item);
|
||||
@ -629,11 +634,11 @@ define('pgadmin.node.server', [
|
||||
},
|
||||
function() {
|
||||
tree.addIcon(_item, {icon: 'icon-server-not-connected'});
|
||||
Notify.pgNotifier('error', error, 'Connection error', gettext('Connect to server.'));
|
||||
pgAdmin.Browser.notifier.pgNotifier('error', error, 'Connection error', gettext('Connect to server.'));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
Notify.pgNotifier('error', error, errormsg, function(msg) {
|
||||
pgAdmin.Browser.notifier.pgNotifier('error', error, errormsg, function(msg) {
|
||||
setTimeout(function() {
|
||||
if (msg == 'CRYPTKEY_SET') {
|
||||
connect_to_server(_node, _data, _tree, _item, _wasConnected);
|
||||
@ -768,7 +773,7 @@ define('pgadmin.node.server', [
|
||||
serverInfo[data._id] = _.extend({}, data);
|
||||
|
||||
if(data.errmsg) {
|
||||
Notify.error(data.errmsg);
|
||||
pgAdmin.Browser.notifier.error(data.errmsg);
|
||||
}
|
||||
// Generate the event that server is connected
|
||||
pgBrowser.Events.trigger(
|
||||
@ -783,7 +788,7 @@ define('pgadmin.node.server', [
|
||||
}else{
|
||||
tree.addIcon(item, {icon: 'icon-server-not-connected'});
|
||||
}
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import pgAdmin from 'sources/pgadmin';
|
||||
import Menu, { MenuItem } from '../../../static/js/helpers/Menu';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import url_for from 'sources/url_for';
|
||||
import Notifier from '../../../static/js/helpers/Notifier';
|
||||
import { getBrowser } from '../../../static/js/utils';
|
||||
import { isMac } from '../../../static/js/keyboard_shortcuts';
|
||||
|
||||
@ -98,7 +97,7 @@ export default class MainMenuFactory {
|
||||
).then(()=>{
|
||||
window.open(options.url);
|
||||
}).catch(()=>{
|
||||
Notifier.error(gettext('Error in opening window'));
|
||||
pgAdmin.Browser.notifier.error(gettext('Error in opening window'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
@ -106,7 +105,6 @@ _.extend(pgBrowser, {
|
||||
if(self.is_inactivity_timeout()) {
|
||||
clearInterval(timeout_daemon_id);
|
||||
self.inactivity_timeout_daemon_running = false;
|
||||
$(window).off('beforeunload');
|
||||
self.logout_inactivity_user();
|
||||
}
|
||||
}, MIN_ACTIVITY_TIME_UNIT);
|
||||
|
@ -7,45 +7,25 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React from 'react';
|
||||
import { generateNodeUrl } from './node_ajax';
|
||||
import MainMenuFactory from './MainMenuFactory';
|
||||
import _ from 'lodash';
|
||||
import Notify, {initializeModalProvider, initializeNotifier} from '../../../static/js/helpers/Notifier';
|
||||
import { checkMasterPassword } from '../../../static/js/Dialogs/index';
|
||||
import { checkMasterPassword, showQuickSearch } from '../../../static/js/Dialogs/index';
|
||||
import { pgHandleItemError } from '../../../static/js/utils';
|
||||
import { Search } from './quick_search/trigger_search';
|
||||
import { send_heartbeat, stop_heartbeat } from './heartbeat';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import { copyToClipboard } from '../../../static/js/clipboard';
|
||||
import { TAB_CHANGE } from './constants';
|
||||
import usePreferences, { setupPreferenceBroadcast } from '../../../preferences/static/js/store';
|
||||
import checkNodeVisibility from '../../../static/js/check_node_visibility';
|
||||
|
||||
define('pgadmin.browser', [
|
||||
'sources/gettext', 'sources/url_for', 'jquery',
|
||||
'sources/pgadmin', 'bundled_codemirror',
|
||||
'sources/check_node_visibility', './toolbar', 'pgadmin.help',
|
||||
'sources/csrf', 'sources/utils', 'sources/window', 'pgadmin.authenticate.kerberos',
|
||||
'sources/tree/tree_init',
|
||||
'pgadmin.browser.utils',
|
||||
'pgadmin.browser.preferences', 'pgadmin.browser.messages',
|
||||
'pgadmin.browser.panel', 'pgadmin.browser.layout',
|
||||
'pgadmin.browser.frame',
|
||||
'sources/gettext', 'sources/url_for', 'sources/pgadmin', 'bundled_codemirror',
|
||||
'sources/csrf', 'pgadmin.authenticate.kerberos',
|
||||
'pgadmin.browser.utils', 'pgadmin.browser.messages',
|
||||
'pgadmin.browser.node', 'pgadmin.browser.collection', 'pgadmin.browser.activity',
|
||||
'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
|
||||
'pgadmin.browser.keyboard', 'sources/tree/pgadmin_tree_save_state',
|
||||
/* wcDocker dependencies */
|
||||
'bootstrap', 'jquery-contextmenu', 'wcdocker',
|
||||
], function(
|
||||
gettext, url_for, $,
|
||||
pgAdmin, codemirror,
|
||||
checkNodeVisibility, toolBar, help, csrfToken, pgadminUtils, pgWindow,
|
||||
Kerberos, InitTree,
|
||||
gettext, url_for, pgAdmin, codemirror, csrfToken, Kerberos,
|
||||
) {
|
||||
window.jQuery = window.$ = $;
|
||||
// Some scripts do export their object in the window only.
|
||||
// Generally the one, which do no have AMD support.
|
||||
let wcDocker = window.wcDocker;
|
||||
$ = $ || window.jQuery || window.$;
|
||||
let CodeMirror = codemirror.default;
|
||||
|
||||
let pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||
@ -55,104 +35,10 @@ define('pgadmin.browser', [
|
||||
|
||||
Kerberos.validate_kerberos_ticket();
|
||||
|
||||
let panelEvents = {};
|
||||
panelEvents[wcDocker.EVENT.VISIBILITY_CHANGED] = function() {
|
||||
if (this.isVisible()) {
|
||||
let obj = pgAdmin.Browser,
|
||||
i = obj.tree ? obj.tree.selected() : undefined,
|
||||
d = i ? obj.tree.itemData(i) : undefined;
|
||||
|
||||
if (d && obj.Nodes[d._type].callbacks['selected'] &&
|
||||
_.isFunction(obj.Nodes[d._type].callbacks['selected'])) {
|
||||
return obj.Nodes[d._type].callbacks['selected'].apply(
|
||||
obj.Nodes[d._type], [i, d, obj, [], '', TAB_CHANGE]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let initializeBrowserTree = pgAdmin.Browser.initializeBrowserTree =
|
||||
function(b) {
|
||||
const draggableTypes = [
|
||||
'collation domain domain_constraints fts_configuration fts_dictionary fts_parser fts_template synonym table partition type sequence package view mview foreign_table edbvar',
|
||||
'schema column database cast event_trigger extension language foreign_data_wrapper foreign_server user_mapping compound_trigger index index_constraint primary_key unique_constraint check_constraint exclusion_constraint foreign_key rule',
|
||||
'trigger trigger_function',
|
||||
'edbfunc function edbproc procedure'
|
||||
];
|
||||
InitTree.initBrowserTree(b).then(() => {
|
||||
const getQualifiedName = (data, item)=>{
|
||||
if(draggableTypes[0].includes(data._type)) {
|
||||
return pgadminUtils.fully_qualify(b, data, item);
|
||||
} else if(draggableTypes[1].includes(data._type)) {
|
||||
return pgadminUtils.quote_ident(data._label);
|
||||
} else if(draggableTypes[3].includes(data._type)) {
|
||||
let newData = {...data};
|
||||
let parsedFunc = pgadminUtils.parseFuncParams(newData._label);
|
||||
newData._label = parsedFunc.func_name;
|
||||
return pgadminUtils.fully_qualify(b, newData, item);
|
||||
} else {
|
||||
return data._label;
|
||||
}
|
||||
};
|
||||
|
||||
b.tree.registerDraggableType({
|
||||
[draggableTypes[0]] : (data, item, treeNodeInfo)=>{
|
||||
let text = getQualifiedName(data, item);
|
||||
return {
|
||||
text: text,
|
||||
objUrl: generateNodeUrl.call(pgBrowser.Nodes[data._type], treeNodeInfo, 'properties', data, true),
|
||||
nodeType: data._type,
|
||||
cur: {
|
||||
from: text.length,
|
||||
to: text.length,
|
||||
},
|
||||
};
|
||||
},
|
||||
[draggableTypes[1]] : (data)=>{
|
||||
return getQualifiedName(data);
|
||||
},
|
||||
[draggableTypes[2]] : (data)=>{
|
||||
return getQualifiedName(data);
|
||||
},
|
||||
[draggableTypes[3]] : (data, item)=>{
|
||||
let parsedFunc = pgadminUtils.parseFuncParams(data._label),
|
||||
dropVal = getQualifiedName(data, item),
|
||||
curPos = {from: 0, to: 0};
|
||||
|
||||
if(parsedFunc.params.length > 0) {
|
||||
dropVal = dropVal + '(';
|
||||
curPos.from = dropVal.length;
|
||||
dropVal = dropVal + parsedFunc.params[0][0];
|
||||
curPos.to = dropVal.length;
|
||||
|
||||
for(let i=1; i<parsedFunc.params.length; i++) {
|
||||
dropVal = dropVal + ', ' + parsedFunc.params[i][0];
|
||||
}
|
||||
|
||||
dropVal = dropVal + ')';
|
||||
} else {
|
||||
dropVal = dropVal + '()';
|
||||
curPos.from = curPos.to = dropVal.length + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
text: dropVal,
|
||||
cur: curPos,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
b.tree.onNodeCopy((data, item)=>{
|
||||
copyToClipboard(getQualifiedName(data, item));
|
||||
});
|
||||
}, () => {console.warn('Tree Load Error');});
|
||||
};
|
||||
|
||||
// Extend the browser class attributes
|
||||
_.extend(pgAdmin.Browser, {
|
||||
// The base url for browser
|
||||
URL: url_for('browser.index'),
|
||||
// We do have docker of type wcDocker to take care of different
|
||||
// containers. (i.e. panels, tabs, frames, etc.)
|
||||
docker:null,
|
||||
// Reversed Engineer query for the selected database node object goes
|
||||
// here
|
||||
@ -215,102 +101,7 @@ define('pgadmin.browser', [
|
||||
},
|
||||
},
|
||||
// Default panels
|
||||
panels: {
|
||||
// Panel to keep the left hand browser tree
|
||||
'browser': new pgAdmin.Browser.Panel({
|
||||
name: 'browser',
|
||||
title: gettext('Object Explorer'),
|
||||
showTitle: true,
|
||||
isCloseable: false,
|
||||
isPrivate: true,
|
||||
icon: '',
|
||||
limit: 1,
|
||||
content: '<div id="tree" class="browser-tree"></div>',
|
||||
onCreate: function(panel, container) {
|
||||
toolBar.initializeToolbar(panel, wcDocker);
|
||||
container.classList.add('pg-no-overflow');
|
||||
},
|
||||
}),
|
||||
// Properties of the object node
|
||||
'properties': new pgAdmin.Browser.Panel({
|
||||
name: 'properties',
|
||||
title: gettext('Properties'),
|
||||
icon: '',
|
||||
width: 500,
|
||||
isCloseable: false,
|
||||
isPrivate: true,
|
||||
elContainer: true,
|
||||
limit: 1,
|
||||
content: '<div class="obj_properties"><div role="status" class="pg-panel-message">' + select_object_msg + '</div></div>',
|
||||
events: panelEvents,
|
||||
onCreate: function(myPanel, container) {
|
||||
container.classList.add('pg-no-overflow');
|
||||
},
|
||||
}),
|
||||
// Statistics of the object
|
||||
'statistics': new pgAdmin.Browser.Panel({
|
||||
name: 'statistics',
|
||||
title: gettext('Statistics'),
|
||||
icon: '',
|
||||
width: 500,
|
||||
isCloseable: true,
|
||||
isPrivate: false,
|
||||
limit : 1,
|
||||
canHide: true,
|
||||
content: '<div></div>',
|
||||
events: panelEvents,
|
||||
}),
|
||||
// Reversed engineered SQL for the object
|
||||
'sql': new pgAdmin.Browser.Panel({
|
||||
name: 'sql',
|
||||
title: gettext('SQL'),
|
||||
icon: '',
|
||||
width: 500,
|
||||
isCloseable: false,
|
||||
isPrivate: true,
|
||||
limit: 1,
|
||||
content: '<div></div>',
|
||||
}),
|
||||
// Dependencies of the object
|
||||
'dependencies': new pgAdmin.Browser.Panel({
|
||||
name: 'dependencies',
|
||||
title: gettext('Dependencies'),
|
||||
icon: '',
|
||||
width: 500,
|
||||
isCloseable: true,
|
||||
isPrivate: false,
|
||||
canHide: true,
|
||||
limit: 1,
|
||||
content: '<div></div>',
|
||||
events: panelEvents,
|
||||
}),
|
||||
// Dependents of the object
|
||||
'dependents': new pgAdmin.Browser.Panel({
|
||||
name: 'dependents',
|
||||
title: gettext('Dependents'),
|
||||
icon: '',
|
||||
width: 500,
|
||||
isCloseable: true,
|
||||
isPrivate: false,
|
||||
limit: 1,
|
||||
canHide: true,
|
||||
content: '<div></div>',
|
||||
events: panelEvents,
|
||||
}),
|
||||
// Background processes
|
||||
'processes': new pgAdmin.Browser.Panel({
|
||||
name: 'processes',
|
||||
title: gettext('Processes'),
|
||||
icon: '',
|
||||
width: 500,
|
||||
isCloseable: true,
|
||||
isPrivate: false,
|
||||
limit: 1,
|
||||
canHide: true,
|
||||
content: '<div></div>',
|
||||
events: panelEvents,
|
||||
}),
|
||||
},
|
||||
panels: {},
|
||||
// We also support showing dashboards, HTML file, external URL
|
||||
frames: {},
|
||||
/* Menus */
|
||||
@ -336,40 +127,6 @@ define('pgadmin.browser', [
|
||||
MainMenus: [],
|
||||
BrowserContextMenu: [],
|
||||
|
||||
add_panels: function() {
|
||||
/* Add hooked-in panels by extensions */
|
||||
let panels = JSON.parse(pgBrowser.panels_items);
|
||||
_.each(panels, function(panel) {
|
||||
if (panel.isIframe) {
|
||||
pgBrowser.frames[panel.name] = new pgBrowser.Frame({
|
||||
name: panel.name,
|
||||
title: panel.title,
|
||||
icon: panel.icon,
|
||||
width: panel.width,
|
||||
height: panel.height,
|
||||
showTitle: panel.showTitle,
|
||||
isCloseable: panel.isCloseable,
|
||||
isPrivate: panel.isPrivate,
|
||||
url: panel.content,
|
||||
});
|
||||
} else {
|
||||
pgBrowser.panels[panel.name] = new pgBrowser.Panel({
|
||||
name: panel.name,
|
||||
title: panel.title,
|
||||
icon: panel.icon,
|
||||
width: panel.width,
|
||||
height: panel.height,
|
||||
showTitle: panel.showTitle,
|
||||
isCloseable: panel.isCloseable,
|
||||
isPrivate: panel.isPrivate,
|
||||
limit: (panel.limit) ? panel.limit : null,
|
||||
content: (panel.content) ? panel.content : '',
|
||||
events: (panel.events) ? panel.events : '',
|
||||
canHide: (panel.canHide) ? panel.canHide : '',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
menu_categories: {
|
||||
/* name, label (pair) */
|
||||
'register': {
|
||||
@ -490,8 +247,6 @@ define('pgadmin.browser', [
|
||||
enable_disable_menus: function(item) {
|
||||
let obj = this;
|
||||
let d = item ? obj.tree.itemData(item) : undefined;
|
||||
toolBar.enable(gettext('View Data'), false);
|
||||
toolBar.enable(gettext('Filtered Rows'), false);
|
||||
|
||||
// All menus (except for the object menus) are already present.
|
||||
// They will just require to check, whether they are
|
||||
@ -529,64 +284,8 @@ define('pgadmin.browser', [
|
||||
obj.initialized = true;
|
||||
|
||||
// Cache preferences
|
||||
obj.cache_preferences();
|
||||
obj.add_panels();
|
||||
// Initialize the Docker
|
||||
obj.docker = new wcDocker(
|
||||
'#dockerContainer', {
|
||||
allowContextMenu: true,
|
||||
allowCollapse: false,
|
||||
loadingClass: 'pg-sp-icon',
|
||||
themePath: url_for('static', {
|
||||
'filename': 'css',
|
||||
}),
|
||||
theme: 'webcabin.overrides.css',
|
||||
});
|
||||
if (obj.docker) {
|
||||
// Initialize all the panels
|
||||
_.each(obj.panels, function(panel, name) {
|
||||
obj.panels[name].load(obj.docker);
|
||||
});
|
||||
// Initialize all the frames
|
||||
_.each(obj.frames, function(frame, name) {
|
||||
obj.frames[name].load(obj.docker);
|
||||
});
|
||||
|
||||
// Stored layout in database from the previous session
|
||||
let layout = pgBrowser.utils.layout;
|
||||
obj.restore_layout(obj.docker, layout, obj.buildDefaultLayout.bind(obj), true);
|
||||
|
||||
obj.docker.on(wcDocker.EVENT.LAYOUT_CHANGED, function() {
|
||||
obj.save_current_layout('Browser/Layout', obj.docker);
|
||||
});
|
||||
}
|
||||
|
||||
initializeBrowserTree(obj);
|
||||
initializeModalProvider();
|
||||
initializeNotifier();
|
||||
|
||||
/* Cache may take time to load for the first time
|
||||
* Reflect the changes once cache is available
|
||||
*/
|
||||
let cacheIntervalId = setInterval(()=> {
|
||||
let sqlEditPreferences = obj.get_preferences_for_module('sqleditor');
|
||||
let browserPreferences = obj.get_preferences_for_module('browser');
|
||||
if(sqlEditPreferences && browserPreferences) {
|
||||
clearInterval(cacheIntervalId);
|
||||
obj.reflectPreferences('sqleditor');
|
||||
obj.reflectPreferences('browser');
|
||||
}
|
||||
}, 500);
|
||||
|
||||
/* Check for sql editor preference changes */
|
||||
obj.onPreferencesChange('sqleditor', function() {
|
||||
obj.reflectPreferences('sqleditor');
|
||||
});
|
||||
|
||||
/* Check for browser preference changes */
|
||||
obj.onPreferencesChange('browser', function() {
|
||||
obj.reflectPreferences('browser');
|
||||
});
|
||||
usePreferences.getState().cache();
|
||||
setupPreferenceBroadcast();
|
||||
|
||||
setTimeout(function() {
|
||||
obj?.editor?.setValue('-- ' + select_object_msg);
|
||||
@ -621,7 +320,6 @@ define('pgadmin.browser', [
|
||||
obj.Events.on('pgadmin:browser:tree:update', obj.onUpdateTreeNode.bind(obj));
|
||||
obj.Events.on('pgadmin:browser:tree:refresh', obj.onRefreshTreeNodeReact.bind(obj));
|
||||
obj.Events.on('pgadmin-browser:tree:loadfail', obj.onLoadFailNode.bind(obj));
|
||||
obj.Events.on('pgadmin-browser:panel-browser:' + wcDocker.EVENT.RESIZE_ENDED, obj.onResizeEnded.bind(obj));
|
||||
obj.bind_beforeunload();
|
||||
|
||||
/* User UI activity */
|
||||
@ -629,8 +327,8 @@ define('pgadmin.browser', [
|
||||
obj.register_to_activity_listener(document);
|
||||
obj.start_inactivity_timeout_daemon();
|
||||
},
|
||||
onResizeEnded: function() {
|
||||
if (this.tree) this.tree.resizeTree();
|
||||
uiloaded: function() {
|
||||
this.check_version_update();
|
||||
},
|
||||
check_corrupted_db_file: function() {
|
||||
getApiInstance().get(
|
||||
@ -638,7 +336,7 @@ define('pgadmin.browser', [
|
||||
).then(({data: res})=> {
|
||||
if(res.data.length > 0) {
|
||||
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
'Warning',
|
||||
'pgAdmin detected unrecoverable corruption in it\'s SQLite configuration database. ' +
|
||||
'The database has been backed up and recreated with default settings. '+
|
||||
@ -649,7 +347,7 @@ define('pgadmin.browser', [
|
||||
);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.alert(error);
|
||||
pgAdmin.Browser.notifier.alert(error);
|
||||
});
|
||||
},
|
||||
check_master_password: function(on_resp_callback) {
|
||||
@ -664,7 +362,7 @@ define('pgadmin.browser', [
|
||||
}
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
});
|
||||
},
|
||||
|
||||
@ -677,7 +375,7 @@ define('pgadmin.browser', [
|
||||
self.set_master_password('');
|
||||
}
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
});
|
||||
},
|
||||
|
||||
@ -695,13 +393,33 @@ define('pgadmin.browser', [
|
||||
checkMasterPassword(data, self.masterpass_callback_queue, cancel_callback);
|
||||
},
|
||||
|
||||
bind_beforeunload: function() {
|
||||
$(window).on('beforeunload', function(e) {
|
||||
/* Can open you in new tab */
|
||||
let openerBrowser = pgWindow.default.pgAdmin.Browser;
|
||||
check_version_update: function() {
|
||||
getApiInstance().get(
|
||||
url_for('misc.upgrade_check')
|
||||
).then((res)=> {
|
||||
const data = res.data.data;
|
||||
if(data.outdated) {
|
||||
pgAdmin.Browser.notifier.warning(
|
||||
`
|
||||
${gettext('You are currently running version %s of %s, <br/>however the current version is %s.', data.current_version, data.product_name, data.upgrade_version)}
|
||||
<br/><br/>
|
||||
${gettext('Please click <a href="%s" target="_new" style="color:inherit">here</a> for more information.', data.download_url)}
|
||||
`,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
let tree_save_interval = pgBrowser.get_preference('browser', 'browser_tree_state_save_interval'),
|
||||
confirm_on_refresh_close = openerBrowser.get_preference('browser', 'confirm_on_refresh_close');
|
||||
}).catch(function() {
|
||||
// Suppress any errors
|
||||
});
|
||||
},
|
||||
|
||||
bind_beforeunload: function() {
|
||||
window.addEventListener('beforeunload', function(e) {
|
||||
/* Can open you in new tab */
|
||||
const prefStore = usePreferences.getState();
|
||||
let tree_save_interval = prefStore.getPreferences('browser', 'browser_tree_state_save_interval'),
|
||||
confirm_on_refresh_close = prefStore.getPreferences('browser', 'confirm_on_refresh_close');
|
||||
|
||||
if (!_.isUndefined(tree_save_interval) && tree_save_interval.value !== -1)
|
||||
pgAdmin.Browser.browserTreeState.save_state();
|
||||
@ -709,7 +427,9 @@ define('pgadmin.browser', [
|
||||
if(!_.isUndefined(confirm_on_refresh_close) && confirm_on_refresh_close.value) {
|
||||
/* This message will not be displayed in Chrome, Firefox, Safari as they have disabled it*/
|
||||
let msg = gettext('Are you sure you want to close the %s browser?', pgBrowser.utils.app_name);
|
||||
e.originalEvent.returnValue = msg;
|
||||
if(e.originalEvent?.returnValue) {
|
||||
e.originalEvent.returnValue = msg;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
});
|
||||
@ -730,8 +450,7 @@ define('pgadmin.browser', [
|
||||
|
||||
// Add menus of module/extension at appropriate menu
|
||||
add_menus: function(menus) {
|
||||
let self = this,
|
||||
pgMenu = this.all_menus_cache;
|
||||
let pgMenu = this.all_menus_cache;
|
||||
|
||||
_.each(menus, function(m) {
|
||||
_.each(m.applies, function(a) {
|
||||
@ -741,12 +460,12 @@ define('pgadmin.browser', [
|
||||
|
||||
// If current node is not visible in browser tree
|
||||
// then return from here
|
||||
if(!checkNodeVisibility(self, m.node)) {
|
||||
if(!checkNodeVisibility(m.node)) {
|
||||
return;
|
||||
} else if(_.has(m, 'module') && !_.isUndefined(m.module)) {
|
||||
// If module to which this menu applies is not visible in
|
||||
// browser tree then also we do not display menu
|
||||
if(!checkNodeVisibility(self, m.module.type)) {
|
||||
if(!checkNodeVisibility(m.module.type)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -770,14 +489,9 @@ define('pgadmin.browser', [
|
||||
}
|
||||
|
||||
// This is to handel quick search callback
|
||||
if(gettext(_m.label) == gettext('Quick Search')) {
|
||||
if(_m.name == 'mnu_quick_search_help') {
|
||||
_m.callback = () => {
|
||||
// Render Search component
|
||||
Notify.showModal(gettext('Quick Search'), (closeModal) => {
|
||||
return <Search closeModal={closeModal}/>;
|
||||
},
|
||||
{ isFullScreen: false, isResizeable: false, showFullScreen: false, isFullWidth: false, showTitle: false}
|
||||
);
|
||||
showQuickSearch();
|
||||
};
|
||||
}
|
||||
|
||||
@ -1683,7 +1397,6 @@ define('pgadmin.browser', [
|
||||
if (!n) {
|
||||
ctx.t.destroy({
|
||||
success: function() {
|
||||
initializeBrowserTree(ctx.b);
|
||||
ctx.t = ctx.b.tree;
|
||||
ctx.i = null;
|
||||
ctx.b._refreshNode(ctx, ctx.branch);
|
||||
@ -1760,7 +1473,7 @@ define('pgadmin.browser', [
|
||||
}
|
||||
}
|
||||
|
||||
Notify.pgNotifier('error', error, gettext('Error retrieving details for the node.'), function (msg) {
|
||||
pgAdmin.Browser.notifier.pgNotifier('error', error, gettext('Error retrieving details for the node.'), function (msg) {
|
||||
if (msg == 'CRYPTKEY_SET') {
|
||||
fetchNodeInfo(__i, __d, __n);
|
||||
} else {
|
||||
@ -1974,7 +1687,7 @@ define('pgadmin.browser', [
|
||||
}
|
||||
fetchNodeInfo(_callback);
|
||||
}).catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
fetchNodeInfo(_callback);
|
||||
});
|
||||
};
|
||||
|
@ -6,7 +6,6 @@
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import {getPanelView} from './panel_view';
|
||||
import _ from 'lodash';
|
||||
|
||||
define([
|
||||
@ -84,15 +83,6 @@ define([
|
||||
canDrop: true,
|
||||
canDropCascade: true,
|
||||
selectParentNodeOnDelete: false,
|
||||
showProperties: function(item, data, panel) {
|
||||
let container = panel.$container.find('.obj_properties').first();
|
||||
getPanelView(
|
||||
pgBrowser.tree,
|
||||
container[0],
|
||||
pgBrowser,
|
||||
panel._type
|
||||
);
|
||||
},
|
||||
generate_url: function(item, type) {
|
||||
/*
|
||||
* Using list, and collection functions of a node to get the nodes
|
||||
|
@ -1,2 +1,34 @@
|
||||
export const AUTH_METHODS = {
|
||||
INTERNAL: 'internal',
|
||||
LDAP: 'ldap',
|
||||
KERBEROS: 'kerberos',
|
||||
OAUTH2: 'oauth2',
|
||||
WEBSERVER: 'webserver'
|
||||
};
|
||||
|
||||
export const TAB_CHANGE = 'TAB_CHANGE';
|
||||
export const TAB_CHANGE = 'TAB_CHANGE';
|
||||
|
||||
export const BROWSER_PANELS = {
|
||||
MAIN: 'id-main',
|
||||
OBJECT_EXPLORER: 'id-object-explorer',
|
||||
DASHBOARD: 'id-dashboard',
|
||||
PROPERTIES: 'id-properties',
|
||||
SQL: 'id-sql',
|
||||
STATISTICS: 'id-statistics',
|
||||
DEPENDENCIES: 'id-dependencies',
|
||||
DEPENDENTS: 'id-dependents',
|
||||
PROCESSES: 'id-processes',
|
||||
PROCESS_DETAILS: 'id-process-details',
|
||||
EDIT_PROPERTIES: 'id-edit-properties',
|
||||
UTILITY_DIALOG: 'id-utility',
|
||||
QUERY_TOOL: 'id-query-tool',
|
||||
PSQL_TOOL: 'id-psql-tool',
|
||||
ERD_TOOL: 'id-erd-tool',
|
||||
SCHEMA_DIFF_TOOL: 'id-schema-diff-tool',
|
||||
DEBUGGER_TOOL: 'id-debugger-tool',
|
||||
CLOUD_WIZARD: 'id-cloud-wizard',
|
||||
GRANT_WIZARD: 'id-grant-wizard',
|
||||
SEARCH_OBJECTS: 'id-search-objects',
|
||||
USER_MANAGEMENT: 'id-user-management',
|
||||
IMPORT_EXPORT_SERVERS: 'id-import-export-servers'
|
||||
};
|
||||
|
@ -1,135 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import _ from 'lodash';
|
||||
define([
|
||||
'sources/pgadmin', 'jquery', 'wcdocker',
|
||||
], function(pgAdmin, $) {
|
||||
|
||||
let pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {},
|
||||
wcDocker = window.wcDocker,
|
||||
wcIFrame = window.wcIFrame;
|
||||
|
||||
pgAdmin.Browser.Frame = function(options) {
|
||||
let defaults = [
|
||||
'name', 'title', 'width', 'height', 'showTitle', 'isCloseable',
|
||||
'isPrivate', 'url', 'icon', 'onCreate', 'isLayoutMember', 'isRenamable',
|
||||
];
|
||||
_.extend(this, _.pick(options, defaults));
|
||||
};
|
||||
|
||||
_.extend(pgAdmin.Browser.Frame.prototype, {
|
||||
name: '',
|
||||
title: '',
|
||||
width: 300,
|
||||
height: 600,
|
||||
showTitle: true,
|
||||
isClosable: true,
|
||||
isRenamable: false,
|
||||
isPrivate: false,
|
||||
isLayoutMember: false,
|
||||
url: '',
|
||||
icon: '',
|
||||
panel: null,
|
||||
frame: null,
|
||||
onCreate: null,
|
||||
load: function(docker) {
|
||||
let that = this;
|
||||
if (!that.panel) {
|
||||
docker.registerPanelType(this.name, {
|
||||
title: that.title,
|
||||
isPrivate: that.isPrivate,
|
||||
isLayoutMember: that.isLayoutMember,
|
||||
onCreate: function(myPanel) {
|
||||
myPanel.initSize(that.width, that.height);
|
||||
|
||||
if (!(myPanel.showTitle??true))
|
||||
myPanel.title(false);
|
||||
|
||||
myPanel.icon(that.icon);
|
||||
|
||||
myPanel.closeable(!!that.isCloseable);
|
||||
myPanel.renamable(that.isRenamable);
|
||||
|
||||
let $frameArea = $('<div style="position:absolute;top:0 !important;width:100%;height:100%;display:table;z-index:0;">');
|
||||
myPanel.layout().addItem($frameArea);
|
||||
that.panel = myPanel;
|
||||
let frame = new wcIFrame($frameArea, myPanel);
|
||||
|
||||
myPanel.frameData = {
|
||||
pgAdminName: that.name,
|
||||
frameInitialized: false,
|
||||
embeddedFrame: frame,
|
||||
};
|
||||
|
||||
if (that.url != '' && that.url != 'about:blank') {
|
||||
setTimeout(function() {
|
||||
frame.openURL(that.url);
|
||||
myPanel.frameData.frameInitialized = true;
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin-browser:frame:urlloaded:' + that.name, frame,
|
||||
that.url, self
|
||||
);
|
||||
}, 50);
|
||||
} else {
|
||||
frame.openURL('about:blank');
|
||||
myPanel.frameData.frameInitialized = true;
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin-browser:frame:urlloaded:' + that.name, frame,
|
||||
that.url, self
|
||||
);
|
||||
}
|
||||
|
||||
if (that.events && _.isObject(that.events)) {
|
||||
_.each(that.events, function(v, k) {
|
||||
if (v && _.isFunction(v)) {
|
||||
myPanel.on(k, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_.each([
|
||||
wcDocker.EVENT.UPDATED, wcDocker.EVENT.VISIBILITY_CHANGED,
|
||||
wcDocker.EVENT.BEGIN_DOCK, wcDocker.EVENT.END_DOCK,
|
||||
wcDocker.EVENT.GAIN_FOCUS, wcDocker.EVENT.LOST_FOCUS,
|
||||
wcDocker.EVENT.CLOSED, wcDocker.EVENT.BUTTON,
|
||||
wcDocker.EVENT.ATTACHED, wcDocker.EVENT.DETACHED,
|
||||
wcDocker.EVENT.MOVE_STARTED, wcDocker.EVENT.MOVE_ENDED,
|
||||
wcDocker.EVENT.MOVED, wcDocker.EVENT.RESIZE_STARTED,
|
||||
wcDocker.EVENT.RESIZE_ENDED, wcDocker.EVENT.RESIZED,
|
||||
wcDocker.EVENT.SCROLLED,
|
||||
], function(ev) {
|
||||
myPanel.on(ev, that.eventFunc.bind(myPanel, ev));
|
||||
});
|
||||
|
||||
if (that.onCreate && _.isFunction(that.onCreate)) {
|
||||
that.onCreate.apply(that, [myPanel, frame]);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
eventFunc: function(eventName) {
|
||||
let name = this.frameData.pgAdminName;
|
||||
|
||||
try {
|
||||
pgBrowser.Events.trigger('pgadmin-browser:frame', eventName, this, arguments);
|
||||
pgBrowser.Events.trigger('pgadmin-browser:frame:' + eventName, this, arguments);
|
||||
|
||||
if (name) {
|
||||
pgBrowser.Events.trigger('pgadmin-browser:frame-' + name, eventName, this, arguments);
|
||||
pgBrowser.Events.trigger('pgadmin-browser:frame-' + name + ':' + eventName, this, arguments);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e.stack || e);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return pgAdmin.Browser.Frame;
|
||||
});
|
@ -10,7 +10,6 @@
|
||||
import gettext from 'sources/gettext';
|
||||
import url_for from 'sources/url_for';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import Notifier from '../../../static/js/helpers/Notifier';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
const axiosApi = getApiInstance();
|
||||
@ -28,9 +27,9 @@ export function send_heartbeat(_server_id, _item) {
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error && error.message == 'Network Error') {
|
||||
Notifier.error(gettext(`pgAdmin server not responding, try to login again: ${error.message || error.response.data.errormsg}`));
|
||||
pgAdmin.Browser.notifier.error(gettext(`pgAdmin server not responding, try to login again: ${error.message || error.response.data.errormsg}`));
|
||||
} else {
|
||||
Notifier.error(gettext(`Server heartbeat logging error: ${error.message || error.response.data.errormsg}`));
|
||||
pgAdmin.Browser.notifier.error(gettext(`Server heartbeat logging error: ${error.message || error.response.data.errormsg}`));
|
||||
}
|
||||
stop_heartbeat(_item);
|
||||
});
|
||||
|
@ -13,6 +13,7 @@ import Mousetrap from 'mousetrap';
|
||||
import * as commonUtils from '../../../static/js/utils';
|
||||
import gettext from 'sources/gettext';
|
||||
import pgWindow from 'sources/window';
|
||||
import usePreferences from '../../../preferences/static/js/store';
|
||||
|
||||
const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||
|
||||
@ -20,51 +21,53 @@ pgBrowser.keyboardNavigation = pgBrowser.keyboardNavigation || {};
|
||||
|
||||
_.extend(pgBrowser.keyboardNavigation, {
|
||||
init: function() {
|
||||
Mousetrap.reset();
|
||||
if (pgBrowser.preferences_cache.length > 0) {
|
||||
this.keyboardShortcut = {
|
||||
...(pgBrowser.get_preference('browser', 'main_menu_file')?.value) && {'file_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'main_menu_file')?.value)},
|
||||
...(pgBrowser.get_preference('browser', 'main_menu_object')?.value) && {'object_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'main_menu_object')?.value)},
|
||||
...(pgBrowser.get_preference('browser', 'main_menu_tools')?.value) && {'tools_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'main_menu_tools')?.value)},
|
||||
...(pgBrowser.get_preference('browser', 'main_menu_help')?.value) && {'help_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'main_menu_help')?.value)},
|
||||
'left_tree_shortcut': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'browser_tree').value),
|
||||
'tabbed_panel_backward': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'tabbed_panel_backward').value),
|
||||
'tabbed_panel_forward': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'tabbed_panel_forward').value),
|
||||
'sub_menu_query_tool': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_query_tool').value),
|
||||
'sub_menu_view_data': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_view_data').value),
|
||||
'sub_menu_search_objects': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_search_objects').value),
|
||||
'sub_menu_properties': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_properties').value),
|
||||
'sub_menu_create': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_create').value),
|
||||
'sub_menu_delete': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_delete').value),
|
||||
'sub_menu_refresh': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_refresh').value),
|
||||
'context_menu': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'context_menu').value),
|
||||
'direct_debugging': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'direct_debugging').value),
|
||||
'add_grid_row': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'add_grid_row').value),
|
||||
'open_quick_search': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'open_quick_search').value),
|
||||
usePreferences.subscribe((prefStore)=>{
|
||||
Mousetrap.reset();
|
||||
if (prefStore.version > 0) {
|
||||
this.keyboardShortcut = {
|
||||
...(prefStore.getPreferences('browser', 'main_menu_file')?.value) && {'file_shortcut': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'main_menu_file')?.value)},
|
||||
...(prefStore.getPreferences('browser', 'main_menu_object')?.value) && {'object_shortcut': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'main_menu_object')?.value)},
|
||||
...(prefStore.getPreferences('browser', 'main_menu_tools')?.value) && {'tools_shortcut': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'main_menu_tools')?.value)},
|
||||
...(prefStore.getPreferences('browser', 'main_menu_help')?.value) && {'help_shortcut': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'main_menu_help')?.value)},
|
||||
'left_tree_shortcut': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'browser_tree').value),
|
||||
'tabbed_panel_backward': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'tabbed_panel_backward').value),
|
||||
'tabbed_panel_forward': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'tabbed_panel_forward').value),
|
||||
'sub_menu_query_tool': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'sub_menu_query_tool').value),
|
||||
'sub_menu_view_data': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'sub_menu_view_data').value),
|
||||
'sub_menu_search_objects': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'sub_menu_search_objects').value),
|
||||
'sub_menu_properties': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'sub_menu_properties').value),
|
||||
'sub_menu_create': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'sub_menu_create').value),
|
||||
'sub_menu_delete': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'sub_menu_delete').value),
|
||||
'sub_menu_refresh': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'sub_menu_refresh').value),
|
||||
'context_menu': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'context_menu').value),
|
||||
'direct_debugging': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'direct_debugging').value),
|
||||
'add_grid_row': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'add_grid_row').value),
|
||||
'open_quick_search': commonUtils.parseShortcutValue(prefStore.getPreferences('browser', 'open_quick_search').value),
|
||||
|
||||
};
|
||||
this.shortcutMethods = {
|
||||
...(pgBrowser.get_preference('browser', 'main_menu_file')?.value) && {'bindMainMenu': {
|
||||
'shortcuts': [this.keyboardShortcut.file_shortcut,
|
||||
this.keyboardShortcut.object_shortcut, this.keyboardShortcut.tools_shortcut,
|
||||
this.keyboardShortcut.help_shortcut],
|
||||
}}, // Main menu
|
||||
'bindRightPanel': {'shortcuts': [this.keyboardShortcut.tabbed_panel_backward, this.keyboardShortcut.tabbed_panel_forward]}, // Main window panels
|
||||
'bindLeftTree': {'shortcuts': this.keyboardShortcut.left_tree_shortcut}, // Main menu,
|
||||
'bindSubMenuQueryTool': {'shortcuts': this.keyboardShortcut.sub_menu_query_tool}, // Sub menu - Open Query Tool,
|
||||
'bindSubMenuViewData': {'shortcuts': this.keyboardShortcut.sub_menu_view_data}, // Sub menu - Open View Data,
|
||||
'bindSubMenuSearchObjects': {'shortcuts': this.keyboardShortcut.sub_menu_search_objects}, // Sub menu - Open search objects,
|
||||
'bindSubMenuProperties': {'shortcuts': this.keyboardShortcut.sub_menu_properties}, // Sub menu - Edit Properties,
|
||||
'bindSubMenuCreate': {'shortcuts': this.keyboardShortcut.sub_menu_create}, // Sub menu - Create Object,
|
||||
'bindSubMenuDelete': {'shortcuts': this.keyboardShortcut.sub_menu_delete}, // Sub menu - Delete object,
|
||||
'bindSubMenuRefresh': {'shortcuts': this.keyboardShortcut.sub_menu_refresh, 'bindElem': '#tree'}, // Sub menu - Refresh object,
|
||||
'bindContextMenu': {'shortcuts': this.keyboardShortcut.context_menu}, // Sub menu - Open context menu,
|
||||
'bindDirectDebugging': {'shortcuts': this.keyboardShortcut.direct_debugging}, // Sub menu - Direct Debugging
|
||||
'bindAddGridRow': {'shortcuts': this.keyboardShortcut.add_grid_row}, // Subnode Grid Add Row
|
||||
'bindOpenQuickSearch': {'shortcuts': this.keyboardShortcut.open_quick_search}, // Subnode Grid Refresh Row
|
||||
};
|
||||
this.bindShortcuts();
|
||||
}
|
||||
};
|
||||
this.shortcutMethods = {
|
||||
...(prefStore.getPreferences('browser', 'main_menu_file')?.value) && {'bindMainMenu': {
|
||||
'shortcuts': [this.keyboardShortcut.file_shortcut,
|
||||
this.keyboardShortcut.object_shortcut, this.keyboardShortcut.tools_shortcut,
|
||||
this.keyboardShortcut.help_shortcut],
|
||||
}}, // Main menu
|
||||
'bindRightPanel': {'shortcuts': [this.keyboardShortcut.tabbed_panel_backward, this.keyboardShortcut.tabbed_panel_forward]}, // Main window panels
|
||||
'bindLeftTree': {'shortcuts': this.keyboardShortcut.left_tree_shortcut}, // Main menu,
|
||||
'bindSubMenuQueryTool': {'shortcuts': this.keyboardShortcut.sub_menu_query_tool}, // Sub menu - Open Query Tool,
|
||||
'bindSubMenuViewData': {'shortcuts': this.keyboardShortcut.sub_menu_view_data}, // Sub menu - Open View Data,
|
||||
'bindSubMenuSearchObjects': {'shortcuts': this.keyboardShortcut.sub_menu_search_objects}, // Sub menu - Open search objects,
|
||||
'bindSubMenuProperties': {'shortcuts': this.keyboardShortcut.sub_menu_properties}, // Sub menu - Edit Properties,
|
||||
'bindSubMenuCreate': {'shortcuts': this.keyboardShortcut.sub_menu_create}, // Sub menu - Create Object,
|
||||
'bindSubMenuDelete': {'shortcuts': this.keyboardShortcut.sub_menu_delete}, // Sub menu - Delete object,
|
||||
'bindSubMenuRefresh': {'shortcuts': this.keyboardShortcut.sub_menu_refresh, 'bindElem': '#tree'}, // Sub menu - Refresh object,
|
||||
'bindContextMenu': {'shortcuts': this.keyboardShortcut.context_menu}, // Sub menu - Open context menu,
|
||||
'bindDirectDebugging': {'shortcuts': this.keyboardShortcut.direct_debugging}, // Sub menu - Direct Debugging
|
||||
'bindAddGridRow': {'shortcuts': this.keyboardShortcut.add_grid_row}, // Subnode Grid Add Row
|
||||
'bindOpenQuickSearch': {'shortcuts': this.keyboardShortcut.open_quick_search}, // Subnode Grid Refresh Row
|
||||
};
|
||||
this.bindShortcuts();
|
||||
}
|
||||
});
|
||||
},
|
||||
bindShortcuts: function() {
|
||||
const self = this;
|
||||
@ -116,7 +119,7 @@ _.extend(pgBrowser.keyboardNavigation, {
|
||||
}
|
||||
|
||||
if(menuLabel) {
|
||||
document.querySelector(`#main-menu-container button[data-label="${menuLabel}"]`)?.click();
|
||||
document.querySelector(`div[data-test="app-menu-bar"] button[data-label="${menuLabel}"]`)?.click();
|
||||
}
|
||||
},
|
||||
bindRightPanel: function(event, combo) {
|
||||
|
@ -1,170 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import url_for from 'sources/url_for';
|
||||
import gettext from 'sources/gettext';
|
||||
import 'wcdocker';
|
||||
import pgWindow from 'sources/window';
|
||||
import Notify from '../../../static/js/helpers/Notifier';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
|
||||
const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||
|
||||
let wcDocker = window.wcDocker;
|
||||
|
||||
/* Add cache related methods and properties */
|
||||
_.extend(pgBrowser, {
|
||||
lock_layout_levels : {
|
||||
PREVENT_DOCKING: 'docking',
|
||||
FULL: 'full',
|
||||
NONE: 'none',
|
||||
},
|
||||
|
||||
// Build the default layout
|
||||
buildDefaultLayout: function(docker) {
|
||||
let browserPanel = docker.addPanel('browser', wcDocker.DOCK.LEFT);
|
||||
let dashboardPanel = docker.addPanel(
|
||||
'dashboard', wcDocker.DOCK.RIGHT, browserPanel);
|
||||
docker.addPanel('properties', wcDocker.DOCK.STACKED, dashboardPanel, {
|
||||
tabOrientation: wcDocker.TAB.TOP,
|
||||
});
|
||||
docker.addPanel('sql', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'statistics', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'dependencies', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'dependents', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'processes', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
},
|
||||
|
||||
save_current_layout: function(layout_id, docker) {
|
||||
if(docker) {
|
||||
let layout = docker.save(),
|
||||
settings = { setting: layout_id, value: layout };
|
||||
|
||||
getApiInstance().post(url_for('settings.store_bulk'), settings);
|
||||
}
|
||||
},
|
||||
|
||||
restore_layout: function(docker, layout, defaultLayoutCallback, checkLayout= false) {
|
||||
// Try to restore the layout if there is one
|
||||
if (layout != '') {
|
||||
try {
|
||||
docker.restore(layout);
|
||||
if(checkLayout) {
|
||||
// Check restore layout is restored pgAdmin 4 layout successfully if not then reset layout to default pgAdmin 4 layout.
|
||||
if((docker.findPanels('properties').length == 0 || docker.findPanels('browser').length == 0) && defaultLayoutCallback){
|
||||
// clear the wcDocker before reset layout.
|
||||
docker.clear();
|
||||
Notify.info(gettext('pgAdmin has reset the layout because the previously saved layout is invalid.'), null);
|
||||
defaultLayoutCallback(docker);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
docker.clear();
|
||||
if(defaultLayoutCallback) {
|
||||
defaultLayoutCallback(docker);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(defaultLayoutCallback) {
|
||||
defaultLayoutCallback(docker);
|
||||
}
|
||||
}
|
||||
|
||||
/* preference available only with top/opener browser. */
|
||||
let browser = pgWindow.pgAdmin.Browser;
|
||||
|
||||
/* interval required initially as preference load may take time */
|
||||
let cacheIntervalId = setInterval(()=> {
|
||||
let browserPref = browser.get_preferences_for_module('browser');
|
||||
if(browserPref) {
|
||||
clearInterval(cacheIntervalId);
|
||||
|
||||
browser.reflectLocklayoutChange(docker);
|
||||
|
||||
browser.onPreferencesChange('browser', function() {
|
||||
browser.reflectLocklayoutChange(docker);
|
||||
});
|
||||
}
|
||||
}, 5000);
|
||||
},
|
||||
|
||||
reflectLocklayoutChange: function(docker) {
|
||||
let browser = pgWindow.pgAdmin.Browser;
|
||||
|
||||
let browserPref = browser.get_preferences_for_module('browser');
|
||||
browser.lock_layout(docker, browserPref.lock_layout);
|
||||
},
|
||||
|
||||
lock_layout: function(docker, op) {
|
||||
let menu_items = [];
|
||||
if('mnu_locklayout' in this.all_menus_cache['file']) {
|
||||
menu_items = this.all_menus_cache['file']['mnu_locklayout']['menu_items'];
|
||||
}
|
||||
|
||||
switch(op) {
|
||||
case this.lock_layout_levels.PREVENT_DOCKING:
|
||||
docker.lockLayout(wcDocker.LOCK_LAYOUT_LEVEL.PREVENT_DOCKING);
|
||||
break;
|
||||
case this.lock_layout_levels.FULL:
|
||||
docker.lockLayout(wcDocker.LOCK_LAYOUT_LEVEL.FULL);
|
||||
break;
|
||||
case this.lock_layout_levels.NONE:
|
||||
docker.lockLayout(wcDocker.LOCK_LAYOUT_LEVEL.NONE);
|
||||
break;
|
||||
}
|
||||
|
||||
if(menu_items) {
|
||||
_.each(menu_items, function(menu_item) {
|
||||
if(menu_item.name != ('mnu_lock_'+op)) {
|
||||
menu_item.change_checked(false);
|
||||
} else {
|
||||
menu_item.change_checked(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
save_lock_layout: function(op) {
|
||||
let browser = pgWindow.pgAdmin.Browser;
|
||||
|
||||
getApiInstance().put(
|
||||
url_for('browser.lock_layout'),
|
||||
JSON.stringify({
|
||||
'value': op,
|
||||
})
|
||||
).then(()=> {
|
||||
browser.cache_preferences('browser');
|
||||
}).catch(function(error) {
|
||||
Notify.pgNotifier('error', error, gettext('Failed to save the lock layout setting.'));
|
||||
});
|
||||
},
|
||||
|
||||
mnu_lock_docking: function() {
|
||||
this.lock_layout(this.docker, this.lock_layout_levels.PREVENT_DOCKING);
|
||||
this.save_lock_layout(this.lock_layout_levels.PREVENT_DOCKING);
|
||||
},
|
||||
|
||||
mnu_lock_full: function() {
|
||||
this.lock_layout(this.docker, this.lock_layout_levels.FULL);
|
||||
this.save_lock_layout(this.lock_layout_levels.FULL);
|
||||
},
|
||||
|
||||
mnu_lock_none: function() {
|
||||
this.lock_layout(this.docker, this.lock_layout_levels.NONE);
|
||||
this.save_lock_layout(this.lock_layout_levels.NONE);
|
||||
},
|
||||
});
|
||||
|
||||
export {pgBrowser};
|
@ -7,12 +7,14 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import {getNodeView, removeNodeView} from './node_view';
|
||||
import Notify from '../../../static/js/helpers/Notifier';
|
||||
import _ from 'lodash';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import { removePanelView } from './panel_view';
|
||||
import { TAB_CHANGE } from './constants';
|
||||
import { BROWSER_PANELS } from './constants';
|
||||
import React from 'react';
|
||||
import ObjectNodeProperties from '../../../misc/properties/ObjectNodeProperties';
|
||||
import ErrorBoundary from '../../../static/js/helpers/ErrorBoundary';
|
||||
import toPx from '../../../static/js/to_px';
|
||||
import usePreferences from '../../../preferences/static/js/store';
|
||||
|
||||
define('pgadmin.browser.node', [
|
||||
'sources/gettext', 'sources/pgadmin',
|
||||
@ -21,9 +23,6 @@ define('pgadmin.browser.node', [
|
||||
], function(
|
||||
gettext, pgAdmin, generateUrl, commonUtils
|
||||
) {
|
||||
|
||||
let wcDocker = window.wcDocker;
|
||||
|
||||
const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||
|
||||
// It has already been defined.
|
||||
@ -89,8 +88,11 @@ define('pgadmin.browser.node', [
|
||||
dialogHelp: '',
|
||||
epasHelp: false,
|
||||
|
||||
title: function(o, d) {
|
||||
return o.label + (d ? (' - ' + d.label) : '');
|
||||
title: function(d, action) {
|
||||
if(action == 'create') {
|
||||
return gettext('Create - %s', this.label);
|
||||
}
|
||||
return d.label??'';
|
||||
},
|
||||
hasId: true,
|
||||
///////
|
||||
@ -282,114 +284,6 @@ define('pgadmin.browser.node', [
|
||||
return true;
|
||||
}
|
||||
},
|
||||
addUtilityPanel: function(width, height, docker) {
|
||||
let body = window.document.body,
|
||||
el = document.createElement('div'),
|
||||
dockerObject = docker || pgBrowser.docker;
|
||||
|
||||
body.insertBefore(el, body.firstChild);
|
||||
|
||||
let new_width = screen.width < 700 ? screen.width * 0.95 : screen.width * 0.5,
|
||||
new_height = screen.height < 500 ? screen.height * 0.95 : screen.height * 0.4;
|
||||
if (!_.isUndefined(width) && !_.isNull(width)) {
|
||||
new_width = width;
|
||||
}
|
||||
if (!_.isUndefined(height) && !_.isNull(height)) {
|
||||
new_height = height;
|
||||
}
|
||||
|
||||
let x = (body.offsetWidth - new_width) / 2;
|
||||
let y = (body.offsetHeight - new_height) / 4;
|
||||
|
||||
let new_panel = dockerObject.addPanel(
|
||||
'utility_props', window.wcDocker.DOCK.FLOAT, undefined, {
|
||||
w: new_width,
|
||||
h: new_height,
|
||||
x: (x),
|
||||
y: (y),
|
||||
}
|
||||
);
|
||||
/*set movable false to prevent dialog from docking,
|
||||
by setting this we can able to move the dialog but can't dock it
|
||||
in to the frame. e.g: can't dock it in to properties and other tabs. */
|
||||
setTimeout(function() {
|
||||
new_panel.moveable(false);
|
||||
}, 0);
|
||||
|
||||
body.removeChild(el);
|
||||
|
||||
return new_panel;
|
||||
},
|
||||
|
||||
registerDockerPanel: function(docker, name, params) {
|
||||
let w = docker || pgBrowser.docker,
|
||||
p = w.findPanels(name);
|
||||
|
||||
if (p && p.length == 1)
|
||||
return;
|
||||
|
||||
p = new pgBrowser.Panel({
|
||||
name: name,
|
||||
showTitle: true,
|
||||
isCloseable: true,
|
||||
isPrivate: true,
|
||||
isLayoutMember: false,
|
||||
canMaximise: true,
|
||||
content: '<div class="obj_properties container-fluid h-100"></div>',
|
||||
...params,
|
||||
});
|
||||
p.load(w);
|
||||
},
|
||||
registerUtilityPanel: function(docker) {
|
||||
let w = docker || pgBrowser.docker,
|
||||
p = w.findPanels('utility_props');
|
||||
|
||||
if (p && p.length == 1)
|
||||
return;
|
||||
|
||||
let events = {};
|
||||
|
||||
p = new pgBrowser.Panel({
|
||||
name: 'utility_props',
|
||||
showTitle: true,
|
||||
isCloseable: true,
|
||||
isPrivate: true,
|
||||
isLayoutMember: false,
|
||||
canMaximise: true,
|
||||
elContainer: true,
|
||||
content: '<div class="obj_properties"></div>',
|
||||
onCreate: function(myPanel, container) {
|
||||
container.classList.add('pg-no-overflow');
|
||||
},
|
||||
events: events,
|
||||
});
|
||||
p.load(w);
|
||||
},
|
||||
register_node_panel: function() {
|
||||
let w = pgBrowser.docker,
|
||||
p = w.findPanels('node_props');
|
||||
|
||||
if (p && p.length == 1)
|
||||
return;
|
||||
|
||||
let events = {};
|
||||
|
||||
p = new pgBrowser.Panel({
|
||||
name: 'node_props',
|
||||
showTitle: true,
|
||||
isCloseable: true,
|
||||
isPrivate: true,
|
||||
isLayoutMember: false,
|
||||
canMaximise: true,
|
||||
elContainer: true,
|
||||
content: '<div class="obj_properties"></div>',
|
||||
onCreate: function(myPanel, container) {
|
||||
container.classList.add('pg-no-overflow');
|
||||
},
|
||||
events: events,
|
||||
});
|
||||
p.load(pgBrowser.docker);
|
||||
},
|
||||
/*
|
||||
* Default script type menu for node.
|
||||
*
|
||||
@ -447,194 +341,132 @@ define('pgadmin.browser.node', [
|
||||
**/
|
||||
show_obj_properties: function(args, item) {
|
||||
let t = pgBrowser.tree,
|
||||
i = (args && args.item) || item || t.selected(),
|
||||
d = i ? t.itemData(i) : undefined,
|
||||
o = this,
|
||||
l = o.title.apply(this, [d]),
|
||||
p;
|
||||
nodeItem = (args && args.item) || item || t.selected(),
|
||||
nodeData = nodeItem ? t.itemData(nodeItem) : undefined,
|
||||
panelTitle = this.title(nodeData, args.action),
|
||||
treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(nodeItem);
|
||||
|
||||
// Make sure - the properties dialog type registered
|
||||
pgBrowser.Node.register_node_panel();
|
||||
|
||||
// No node selected.
|
||||
if (!d)
|
||||
if (!nodeData)
|
||||
return;
|
||||
|
||||
let self = this,
|
||||
isParent = (_.isArray(this.parent_type) ?
|
||||
function(_d) {
|
||||
return (_.indexOf(self.parent_type, _d._type) != -1);
|
||||
} : function(_d) {
|
||||
return (self.parent_type == _d._type);
|
||||
}),
|
||||
addPanel = function() {
|
||||
let body = window.document.body,
|
||||
el = document.createElement('div');
|
||||
|
||||
body.insertBefore(el, body.firstChild);
|
||||
|
||||
let w, h, x, y;
|
||||
if(screen.width < 800) {
|
||||
w = pgAdmin.toPx(el, '95%', 'width', true);
|
||||
} else {
|
||||
w = pgAdmin.toPx(
|
||||
el, self.width || (pgBrowser.stdW.default + 'px'),
|
||||
'width', true
|
||||
);
|
||||
|
||||
/* Fit to standard sizes */
|
||||
if(w <= pgBrowser.stdW.sm) {
|
||||
w = pgBrowser.stdW.sm;
|
||||
} else {
|
||||
if(w <= pgBrowser.stdW.md) {
|
||||
w = pgBrowser.stdW.md;
|
||||
} else {
|
||||
w = pgBrowser.stdW.lg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(screen.height < 600) {
|
||||
h = pgAdmin.toPx(el, '95%', 'height', true);
|
||||
} else {
|
||||
h = pgAdmin.toPx(
|
||||
el, self.height || (pgBrowser.stdH.default + 'px'),
|
||||
'height', true
|
||||
);
|
||||
|
||||
/* Fit to standard sizes */
|
||||
if(h <= pgBrowser.stdH.sm) {
|
||||
h = pgBrowser.stdH.sm;
|
||||
} else {
|
||||
if(h <= pgBrowser.stdH.md) {
|
||||
h = pgBrowser.stdH.md;
|
||||
} else {
|
||||
h = pgBrowser.stdH.lg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x = (body.offsetWidth - w) / 2;
|
||||
y = (body.offsetHeight - h) / 4;
|
||||
|
||||
// If the screen resolution is higher, but - it is zoomed, dialog
|
||||
// may be go out of window, and will not be accessible through the
|
||||
// keyboard.
|
||||
if (w > window.innerWidth) {
|
||||
x = 0;
|
||||
w = window.innerWidth;
|
||||
}
|
||||
if (h > window.innerHeight) {
|
||||
y = 0;
|
||||
h = window.innerHeight;
|
||||
}
|
||||
|
||||
let new_panel = pgBrowser.docker.addPanel(
|
||||
'node_props', wcDocker.DOCK.FLOAT, undefined, {
|
||||
w: w + 'px',
|
||||
h: h + 'px',
|
||||
x: x + 'px',
|
||||
y: y + 'px',
|
||||
}
|
||||
);
|
||||
|
||||
body.removeChild(el);
|
||||
|
||||
return new_panel;
|
||||
};
|
||||
const isParent = (_.isArray(this.parent_type) ?
|
||||
(_d)=>{
|
||||
return (_.indexOf(this.parent_type, _d._type) != -1);
|
||||
} : (_d)=>{
|
||||
return (this.parent_type == _d._type);
|
||||
});
|
||||
|
||||
if (args.action == 'create') {
|
||||
// If we've parent, we will get the information of it for
|
||||
// proper object manipulation.
|
||||
//
|
||||
// You know - we're working with RDBMS, relation is everything
|
||||
// for us.
|
||||
if (self.parent_type && !isParent(d)) {
|
||||
// In browser tree, I can be under any node, But - that
|
||||
// does not mean, it is my parent.
|
||||
//
|
||||
// We have some group nodes too.
|
||||
//
|
||||
// i.e.
|
||||
// Tables, Views, etc. nodes under Schema node
|
||||
//
|
||||
// And, actual parent of a table is schema, not Tables.
|
||||
while (i && t.hasParent(i)) {
|
||||
i = t.parent(i);
|
||||
let pd = t.itemData(i);
|
||||
if (this.parent_type && !isParent(nodeData)) {
|
||||
// actual parent of a table is schema, not Tables.
|
||||
while (nodeItem && t.hasParent(nodeItem)) {
|
||||
nodeItem = t.parent(nodeItem);
|
||||
let pd = t.itemData(nodeItem);
|
||||
|
||||
if (isParent(pd)) {
|
||||
// Assign the data, this is my actual parent.
|
||||
d = pd;
|
||||
nodeData = pd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Seriously - I really don't have parent data present?
|
||||
//
|
||||
// The only node - which I know - who does not have parent
|
||||
// node, is the Server Group (and, comes directly under root
|
||||
// node - which has no parent.)
|
||||
if (!d || (this.parent_type != null && !isParent(d))) {
|
||||
// The only node who does not have parent is the Server Group
|
||||
if (!nodeData || (this.parent_type != null && !isParent(nodeData))) {
|
||||
// It should never come here.
|
||||
// If it is here, that means - we do have some bug in code.
|
||||
return;
|
||||
}
|
||||
|
||||
l = gettext('Create - %s', this.label);
|
||||
if (this.type == 'server') {
|
||||
l = gettext('Register - %s', this.label);
|
||||
}
|
||||
p = addPanel();
|
||||
|
||||
setTimeout(function() {
|
||||
o.showProperties(i, d, p, args.action);
|
||||
}, 10);
|
||||
} else {
|
||||
if (pgBrowser.Node.panels && pgBrowser.Node.panels[d.id] &&
|
||||
pgBrowser.Node.panels[d.id].$container) {
|
||||
p = pgBrowser.Node.panels[d.id];
|
||||
/** TODO ::
|
||||
* Run in edit mode (if asked) only when it is
|
||||
* not already been running edit mode
|
||||
**/
|
||||
let mode = p.$container.attr('action-mode');
|
||||
if (mode) {
|
||||
let msg = gettext('Are you sure want to stop editing the properties of %s "%s"?');
|
||||
if (args.action == 'edit') {
|
||||
msg = gettext('Are you sure want to reset the current changes and re-open the panel for %s "%s"?');
|
||||
}
|
||||
|
||||
Notify.confirm(
|
||||
gettext('Edit in progress?'),
|
||||
commonUtils.sprintf(msg, o.label.toLowerCase(), d.label),
|
||||
function() {
|
||||
setTimeout(function() {
|
||||
o.showProperties(i, d, p, args.action);
|
||||
}, 10);
|
||||
},
|
||||
null).show();
|
||||
} else {
|
||||
setTimeout(function() {
|
||||
o.showProperties(i, d, p, args.action);
|
||||
}, 10);
|
||||
const panelId = _.uniqueId(BROWSER_PANELS.EDIT_PROPERTIES);
|
||||
const onClose = (force=false)=>pgBrowser.docker.close(panelId, force);
|
||||
const onSave = (newNodeData)=>{
|
||||
// Clear the cache for this node now.
|
||||
setTimeout(()=>{
|
||||
this.clear_cache.apply(this, item);
|
||||
}, 0);
|
||||
try {
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin:browser:tree:add', _.clone(newNodeData.node),
|
||||
_.clone(treeNodeInfo)
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn(e.stack || e);
|
||||
}
|
||||
} else {
|
||||
pgBrowser.Node.panels = pgBrowser.Node.panels || {};
|
||||
p = pgBrowser.Node.panels[d.id] = addPanel();
|
||||
onClose();
|
||||
};
|
||||
this.showPropertiesDialog(panelId, panelTitle, {
|
||||
treeNodeInfo: treeNodeInfo,
|
||||
item: nodeItem,
|
||||
nodeData: nodeData,
|
||||
actionType: 'create',
|
||||
onSave: onSave,
|
||||
onClose: onClose,
|
||||
});
|
||||
} else {
|
||||
const panelId = BROWSER_PANELS.EDIT_PROPERTIES+nodeData.id;
|
||||
const onClose = (force=false)=>pgBrowser.docker.close(panelId, force);
|
||||
const onSave = (newNodeData)=>{
|
||||
let _old = nodeData,
|
||||
_new = newNodeData.node,
|
||||
info = treeNodeInfo;
|
||||
|
||||
setTimeout(function() {
|
||||
o.showProperties(i, d, p, args.action);
|
||||
}, 10);
|
||||
// Clear the cache for this node now.
|
||||
setTimeout(()=>{
|
||||
this.clear_cache.apply(this, item);
|
||||
}, 0);
|
||||
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin:browser:tree:update',
|
||||
_old, _new, info, {
|
||||
success: function(_item, _newNodeData, _oldNodeData) {
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin:browser:node:updated', _item, _newNodeData,
|
||||
_oldNodeData
|
||||
);
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin:browser:node:' + _newNodeData._type + ':updated',
|
||||
_item, _newNodeData, _oldNodeData
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
onClose();
|
||||
};
|
||||
if(pgBrowser.docker.find(panelId)) {
|
||||
let msg = gettext('Are you sure want to stop editing the properties of %s "%s"?');
|
||||
if (args.action == 'edit') {
|
||||
msg = gettext('Are you sure want to reset the current changes and re-open the panel for %s "%s"?');
|
||||
}
|
||||
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Edit in progress?'),
|
||||
commonUtils.sprintf(msg, this.label.toLowerCase(), nodeData.label),
|
||||
()=>{
|
||||
this.showPropertiesDialog(panelId, panelTitle, {
|
||||
treeNodeInfo: treeNodeInfo,
|
||||
item: nodeItem,
|
||||
nodeData: nodeData,
|
||||
actionType: 'edit',
|
||||
onSave: onSave,
|
||||
onClose: onClose,
|
||||
}, true);
|
||||
},
|
||||
null
|
||||
);
|
||||
} else {
|
||||
this.showPropertiesDialog(panelId, panelTitle, {
|
||||
treeNodeInfo: treeNodeInfo,
|
||||
item: nodeItem,
|
||||
nodeData: nodeData,
|
||||
actionType: 'edit',
|
||||
onSave: onSave,
|
||||
onClose: onClose,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
p.title(l);
|
||||
p.icon('icon-' + this.type);
|
||||
|
||||
// Make sure the properties dialog is visible
|
||||
p.focus();
|
||||
},
|
||||
// Delete the selected object
|
||||
delete_obj: function(args, item) {
|
||||
@ -668,7 +500,7 @@ define('pgadmin.browser.node', [
|
||||
|
||||
if (!(_.isFunction(obj.canDropCascade) ?
|
||||
obj.canDropCascade.apply(obj, [d, i]) : obj.canDropCascade)) {
|
||||
Notify.error(
|
||||
pgAdmin.Browser.notifier.error(
|
||||
gettext('The %s "%s" cannot be dropped.', obj.label, d.label),
|
||||
10000
|
||||
);
|
||||
@ -685,24 +517,24 @@ define('pgadmin.browser.node', [
|
||||
|
||||
if (!(_.isFunction(obj.canDrop) ?
|
||||
obj.canDrop.apply(obj, [d, i]) : obj.canDrop)) {
|
||||
Notify.error(
|
||||
pgAdmin.Browser.notifier.error(
|
||||
gettext('The %s "%s" cannot be dropped/removed.', obj.label, d.label),
|
||||
10000
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Notify.confirm(title, msg,
|
||||
pgAdmin.Browser.notifier.confirm(title, msg,
|
||||
function() {
|
||||
getApiInstance().delete(
|
||||
obj.generate_url(i, input.url, d, true),
|
||||
).then(({data: res})=> {
|
||||
if(res.success == 2){
|
||||
Notify.error(res.info, null);
|
||||
pgAdmin.Browser.notifier.error(res.info, null);
|
||||
return;
|
||||
}
|
||||
if (res.success == 0) {
|
||||
Notify.alert(res.errormsg, res.info);
|
||||
pgAdmin.Browser.notifier.alert(res.errormsg, res.info);
|
||||
} else {
|
||||
// Remove the node from tree and set collection node as selected.
|
||||
let selectNextNode = true;
|
||||
@ -727,7 +559,7 @@ define('pgadmin.browser.node', [
|
||||
console.warn(e.stack || e);
|
||||
}
|
||||
}
|
||||
Notify.alert(gettext('Error dropping/removing %s: "%s"', obj.label, objName), errmsg);
|
||||
pgAdmin.Browser.notifier.alert(gettext('Error dropping/removing %s: "%s"', obj.label, objName), errmsg);
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -773,7 +605,7 @@ define('pgadmin.browser.node', [
|
||||
|
||||
// Callback to render query editor
|
||||
show_query_tool: function(args, item) {
|
||||
let preference = pgBrowser.get_preference('sqleditor', 'copy_sql_to_query_tool');
|
||||
let preference = usePreferences.getState().getPreferences('sqleditor', 'copy_sql_to_query_tool');
|
||||
let t = pgBrowser.tree,
|
||||
i = item || t.selected(),
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
@ -862,7 +694,7 @@ define('pgadmin.browser.node', [
|
||||
pgBrowser.Node.callbacks.change_server_background(item, data);
|
||||
},
|
||||
// Callback called - when a node is selected in browser tree.
|
||||
selected: function(item, data, browser, _argsList, _event, actionSource) {
|
||||
selected: function(item, data, browser) {
|
||||
// Show the information about the selected node in the below panels,
|
||||
// which are visible at this time:
|
||||
// + Properties
|
||||
@ -870,34 +702,11 @@ define('pgadmin.browser.node', [
|
||||
// + Dependents
|
||||
// + Dependencies
|
||||
// + Statistics
|
||||
let b = browser || pgBrowser,
|
||||
t = b.tree,
|
||||
d = data || t.itemData(item);
|
||||
let b = browser || pgBrowser;
|
||||
|
||||
// Update the menu items
|
||||
pgAdmin.Browser.enable_disable_menus.apply(b, [item]);
|
||||
|
||||
if (d && b) {
|
||||
|
||||
if ('properties' in b.panels &&
|
||||
b.panels['properties'] &&
|
||||
b.panels['properties'].panel) {
|
||||
|
||||
if (actionSource != TAB_CHANGE) {
|
||||
const propertiesPanel = b.panels['properties'].panel.$container.find('.obj_properties').first();
|
||||
if (propertiesPanel) {
|
||||
removePanelView(propertiesPanel[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (b.panels['properties'].panel.isVisible()) {
|
||||
this.showProperties(item, d, b.panels['properties'].panel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pgBrowser.Events.trigger('pgadmin:browser:tree:update-tree-state',
|
||||
item);
|
||||
return true;
|
||||
@ -922,7 +731,7 @@ define('pgadmin.browser.node', [
|
||||
},
|
||||
opened: function(item) {
|
||||
let tree = pgBrowser.tree,
|
||||
auto_expand = pgBrowser.get_preference('browser', 'auto_expand_sole_children');
|
||||
auto_expand = usePreferences.getState().getPreferences('browser', 'auto_expand_sole_children');
|
||||
|
||||
if (auto_expand && auto_expand.value && tree.children(item).length == 1) {
|
||||
// Automatically expand the child node, if a treeview node has only a single child.
|
||||
@ -957,127 +766,55 @@ define('pgadmin.browser.node', [
|
||||
item);
|
||||
},
|
||||
},
|
||||
/**********************************************************************
|
||||
* A hook (not a callback) to show object properties in given HTML
|
||||
* element.
|
||||
*
|
||||
* This has been used for the showing, editing properties of the node.
|
||||
* This has also been used for creating a node.
|
||||
**/
|
||||
showProperties: function(item, data, panel, action) {
|
||||
let that = this,
|
||||
j = panel.$container.find('.obj_properties').first();
|
||||
showPropertiesDialog: function(panelId, panelTitle, dialogProps, update=false) {
|
||||
const panelData = {
|
||||
id: panelId,
|
||||
title: panelTitle,
|
||||
manualClose: true,
|
||||
icon: `dialog-node-icon ${this.node_image?.(dialogProps.itemNodeData) ?? ('icon-' + this.type)}`,
|
||||
content: (
|
||||
<ErrorBoundary>
|
||||
<ObjectNodeProperties
|
||||
panelId={panelId}
|
||||
node={this}
|
||||
formType="dialog"
|
||||
{...dialogProps}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
};
|
||||
|
||||
// Callback to show object properties
|
||||
let properties = function() {
|
||||
let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item);
|
||||
getNodeView(
|
||||
that.type, treeNodeInfo, 'properties', data, 'tab', j[0], this, onEdit
|
||||
);
|
||||
return;
|
||||
}.bind(panel),
|
||||
let w = toPx(this.width || (pgBrowser.stdW.default + 'px'), 'width', true);
|
||||
let h = toPx(this.height || (pgBrowser.stdH.default + 'px'), 'height', true);
|
||||
|
||||
editFunc = function() {
|
||||
let self = this;
|
||||
if (action && action == 'properties') {
|
||||
action = 'edit';
|
||||
}
|
||||
self.$container.attr('action-mode', action);
|
||||
|
||||
self.icon(
|
||||
_.isFunction(that['node_image']) ?
|
||||
(that['node_image']).apply(that, [data]) :
|
||||
(that['node_image'] || ('icon-' + that.type))
|
||||
);
|
||||
/* Remove any dom rendered by getNodeView */
|
||||
removeNodeView(j[0]);
|
||||
/* getSchema is a schema for React. Get the react node view */
|
||||
let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item);
|
||||
getNodeView(
|
||||
that.type, treeNodeInfo, action, data, 'dialog', j[0], this, onEdit,
|
||||
(nodeData)=>{
|
||||
if(nodeData.node) {
|
||||
onSaveFunc(nodeData.node, treeNodeInfo);
|
||||
if(nodeData.success === 0) {
|
||||
Notify.alert(gettext('Error'),
|
||||
gettext(nodeData.errormsg)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
return;
|
||||
|
||||
}.bind(panel),
|
||||
|
||||
updateTreeItem = function(obj, tnode, node_info) {
|
||||
let _old = data,
|
||||
_new = tnode,
|
||||
info = node_info;
|
||||
|
||||
// Clear the cache for this node now.
|
||||
setTimeout(function() {
|
||||
obj.clear_cache.apply(obj, item);
|
||||
}, 0);
|
||||
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin:browser:tree:update',
|
||||
_old, _new, info, {
|
||||
success: function(_item, _newNodeData, _oldNodeData) {
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin:browser:node:updated', _item, _newNodeData,
|
||||
_oldNodeData
|
||||
);
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin:browser:node:' + _newNodeData._type + ':updated',
|
||||
_item, _newNodeData, _oldNodeData
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
this.close();
|
||||
},
|
||||
saveNewNode = function(obj, tnode, node_info) {
|
||||
// Clear the cache for this node now.
|
||||
setTimeout(function() {
|
||||
obj.clear_cache.apply(obj, item);
|
||||
}, 0);
|
||||
try {
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin:browser:tree:add', _.clone(tnode),
|
||||
_.clone(node_info)
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn(e.stack || e);
|
||||
}
|
||||
this.close();
|
||||
}.bind(panel, that),
|
||||
editInNewPanel = function() {
|
||||
// Open edit in separate panel
|
||||
setTimeout(function() {
|
||||
that.callbacks.show_obj_properties.apply(that, [{
|
||||
'action': 'edit',
|
||||
'item': item,
|
||||
}]);
|
||||
}, 0);
|
||||
},
|
||||
onSaveFunc = updateTreeItem.bind(panel, that),
|
||||
onEdit = editFunc.bind(panel);
|
||||
|
||||
if (action) {
|
||||
if (action == 'create') {
|
||||
onSaveFunc = saveNewNode;
|
||||
}
|
||||
if (action != 'properties') {
|
||||
// We need to keep track edit/create mode for this panel.
|
||||
editFunc();
|
||||
} else {
|
||||
properties();
|
||||
}
|
||||
/* Fit to standard sizes */
|
||||
if(w <= pgBrowser.stdW.sm) {
|
||||
w = pgBrowser.stdW.sm;
|
||||
} else {
|
||||
/* Show properties */
|
||||
onEdit = editInNewPanel.bind(panel);
|
||||
properties();
|
||||
if(w <= pgBrowser.stdW.md) {
|
||||
w = pgBrowser.stdW.md;
|
||||
} else {
|
||||
w = pgBrowser.stdW.lg;
|
||||
}
|
||||
}
|
||||
|
||||
if(h <= pgBrowser.stdH.sm) {
|
||||
h = pgBrowser.stdH.sm;
|
||||
} else {
|
||||
if(h <= pgBrowser.stdH.md) {
|
||||
h = pgBrowser.stdH.md;
|
||||
} else {
|
||||
h = pgBrowser.stdH.lg;
|
||||
}
|
||||
}
|
||||
|
||||
if(update) {
|
||||
dialogProps.onClose(true);
|
||||
setTimeout(()=>{
|
||||
pgBrowser.docker.openDialog(panelData, w, h);
|
||||
}, 10);
|
||||
} else {
|
||||
pgBrowser.docker.openDialog(panelData, w, h);
|
||||
}
|
||||
},
|
||||
_find_parent_node: function(t, i, d) {
|
||||
|
@ -1,225 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import {getHelpUrl, getEPASHelpUrl} from 'pgadmin.help';
|
||||
import SchemaView from 'sources/SchemaView';
|
||||
import { generateNodeUrl } from './node_ajax';
|
||||
import Notify from '../../../static/js/helpers/Notifier';
|
||||
import gettext from 'sources/gettext';
|
||||
import 'wcdocker';
|
||||
import Theme from '../../../static/js/Theme';
|
||||
|
||||
/* The entry point for rendering React based view in properties, called in node.js */
|
||||
export function getNodeView(nodeType, treeNodeInfo, actionType, itemNodeData, formType, container, containerPanel, onEdit, onSave) {
|
||||
let nodeObj = pgAdmin.Browser.Nodes[nodeType];
|
||||
let serverInfo = treeNodeInfo && ('server' in treeNodeInfo) &&
|
||||
pgAdmin.Browser.serverInfo && pgAdmin.Browser.serverInfo[treeNodeInfo.server._id];
|
||||
let inCatalog = treeNodeInfo && ('catalog' in treeNodeInfo);
|
||||
let urlBase = generateNodeUrl.call(nodeObj, treeNodeInfo, actionType, itemNodeData, false, nodeObj.url_jump_after_node);
|
||||
const api = getApiInstance();
|
||||
const url = (isNew)=>{
|
||||
return urlBase + (isNew ? '' : itemNodeData._id);
|
||||
};
|
||||
let isDirty = false; // usefull for warnings
|
||||
let warnOnCloseFlag = true;
|
||||
const confirmOnCloseReset = pgAdmin.Browser.get_preferences_for_module('browser').confirm_on_properties_close;
|
||||
let updatedData = ['table', 'partition'].includes(nodeType) && !_.isEmpty(itemNodeData.rows_cnt) ? {rows_cnt: itemNodeData.rows_cnt} : undefined;
|
||||
|
||||
let onError = (err)=> {
|
||||
if(err.response){
|
||||
console.error('error resp', err.response);
|
||||
} else if(err.request){
|
||||
console.error('error req', err.request);
|
||||
} else if(err.message){
|
||||
console.error('error msg', err.message);
|
||||
}
|
||||
};
|
||||
|
||||
/* Called when dialog is opened in edit mode, promise required */
|
||||
let initData = ()=>new Promise((resolve, reject)=>{
|
||||
if(actionType === 'create') {
|
||||
resolve({});
|
||||
} else {
|
||||
api.get(url(false))
|
||||
.then((res)=>{
|
||||
resolve(res.data);
|
||||
})
|
||||
.catch((err)=>{
|
||||
Notify.pgNotifier('error', err, '', function(msg) {
|
||||
if (msg == 'CRYPTKEY_SET') {
|
||||
return Promise.resolve(initData());
|
||||
} else if (msg == 'CRYPTKEY_NOT_SET') {
|
||||
reject(gettext('The master password is not set.'));
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* on save button callback, promise required */
|
||||
const onSaveClick = (isNew, data)=>new Promise((resolve, reject)=>{
|
||||
return api({
|
||||
url: url(isNew),
|
||||
method: isNew ? 'POST' : 'PUT',
|
||||
data: data,
|
||||
}).then((res)=>{
|
||||
/* Don't warn the user before closing dialog */
|
||||
warnOnCloseFlag = false;
|
||||
resolve(res.data);
|
||||
onSave && onSave(res.data);
|
||||
}).catch((err)=>{
|
||||
Notify.pgNotifier('error-noalert', err, '', function(msg) {
|
||||
if (msg == 'CRYPTKEY_SET') {
|
||||
return Promise.resolve(onSaveClick(isNew, data));
|
||||
} else if (msg == 'CRYPTKEY_NOT_SET') {
|
||||
reject(gettext('The master password is not set.'));
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/* Called when switched to SQL tab, promise required */
|
||||
const getSQLValue = (isNew, changedData)=>{
|
||||
const msqlUrl = generateNodeUrl.call(nodeObj, treeNodeInfo, 'msql', itemNodeData, !isNew, nodeObj.url_jump_after_node);
|
||||
return new Promise((resolve, reject)=>{
|
||||
api({
|
||||
url: msqlUrl,
|
||||
method: 'GET',
|
||||
params: changedData,
|
||||
}).then((res)=>{
|
||||
resolve(res.data.data);
|
||||
}).catch((err)=>{
|
||||
onError(err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/* Callback for help button */
|
||||
const onHelp = (isSqlHelp=false, isNew=false)=>{
|
||||
if(isSqlHelp) {
|
||||
let server = treeNodeInfo.server;
|
||||
let helpUrl = pgAdmin.Browser.utils.pg_help_path;
|
||||
let fullUrl = '';
|
||||
|
||||
if (server.server_type == 'ppas' && nodeObj.epasHelp) {
|
||||
fullUrl = getEPASHelpUrl(server.version);
|
||||
} else {
|
||||
if (nodeObj.sqlCreateHelp == '' && nodeObj.sqlAlterHelp != '') {
|
||||
fullUrl = getHelpUrl(helpUrl, nodeObj.sqlAlterHelp, server.version);
|
||||
} else if (nodeObj.sqlCreateHelp != '' && nodeObj.sqlAlterHelp == '') {
|
||||
fullUrl = getHelpUrl(helpUrl, nodeObj.sqlCreateHelp, server.version);
|
||||
} else {
|
||||
if (isNew) {
|
||||
fullUrl = getHelpUrl(helpUrl, nodeObj.sqlCreateHelp, server.version);
|
||||
} else {
|
||||
fullUrl = getHelpUrl(helpUrl, nodeObj.sqlAlterHelp, server.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.open(fullUrl, 'postgres_help');
|
||||
} else {
|
||||
window.open(nodeObj.dialogHelp, 'pgadmin_help');
|
||||
}
|
||||
};
|
||||
|
||||
/* A warning before closing the dialog with unsaved changes, based on preference */
|
||||
let warnBeforeChangesLost = (yesCallback)=>{
|
||||
let confirmOnClose = pgAdmin.Browser.get_preferences_for_module('browser').confirm_on_properties_close;
|
||||
if (warnOnCloseFlag && confirmOnClose) {
|
||||
if(isDirty){
|
||||
Notify.confirm(
|
||||
gettext('Warning'),
|
||||
gettext('Changes will be lost. Are you sure you want to close the dialog?'),
|
||||
function() {
|
||||
yesCallback();
|
||||
return true;
|
||||
},
|
||||
function() {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
yesCallback();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/* Bind the wcDocker dialog close event and check if user should be warned */
|
||||
if (containerPanel.closeable()) {
|
||||
containerPanel.on(window.wcDocker.EVENT.CLOSING, warnBeforeChangesLost.bind(
|
||||
containerPanel,
|
||||
function() {
|
||||
containerPanel.off(window.wcDocker.EVENT.CLOSING);
|
||||
/* Always clean up the react mounted dom before closing */
|
||||
removeNodeView(container);
|
||||
containerPanel.close();
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
/* All other useful details can go with this object */
|
||||
const viewHelperProps = {
|
||||
mode: actionType,
|
||||
serverInfo: serverInfo ? {
|
||||
type: serverInfo.server_type,
|
||||
version: serverInfo.version,
|
||||
}: undefined,
|
||||
inCatalog: inCatalog,
|
||||
};
|
||||
|
||||
let schema = nodeObj.getSchema.call(nodeObj, treeNodeInfo, itemNodeData);
|
||||
// Show/Hide security group for nodes under the catalog
|
||||
if('catalog' in treeNodeInfo
|
||||
&& formType !== 'tab') {
|
||||
schema.filterGroups = [gettext('Security')];
|
||||
}
|
||||
|
||||
/* Fire at will, mount the DOM */
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<SchemaView
|
||||
key={itemNodeData?._id}
|
||||
formType={formType}
|
||||
getInitData={initData}
|
||||
updatedData={updatedData}
|
||||
schema={schema}
|
||||
viewHelperProps={viewHelperProps}
|
||||
onSave={onSaveClick}
|
||||
onClose={()=>containerPanel.close()}
|
||||
onHelp={onHelp}
|
||||
onEdit={onEdit}
|
||||
onDataChange={(dataChanged)=>{
|
||||
isDirty = dataChanged;
|
||||
}}
|
||||
confirmOnCloseReset={confirmOnCloseReset}
|
||||
hasSQL={nodeObj.hasSQL && (actionType === 'create' || actionType === 'edit')}
|
||||
getSQLValue={getSQLValue}
|
||||
disableSqlHelp={nodeObj.sqlAlterHelp == '' && nodeObj.sqlCreateHelp == '' && !nodeObj.epasHelp}
|
||||
disableDialogHelp={nodeObj.dialogHelp == undefined || nodeObj.dialogHelp == ''}
|
||||
/>
|
||||
</Theme>, container);
|
||||
}
|
||||
|
||||
/* When switching from normal node to collection node, clean up the React mounted DOM */
|
||||
export function removeNodeView(container) {
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
}
|
@ -1,290 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { getPanelView } from './panel_view';
|
||||
import _ from 'lodash';
|
||||
|
||||
define(
|
||||
['sources/pgadmin', 'wcdocker'],
|
||||
function(pgAdmin) {
|
||||
|
||||
let pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {},
|
||||
wcDocker = window.wcDocker;
|
||||
|
||||
pgAdmin.Browser.Panel = function(options) {
|
||||
let defaults = [
|
||||
'name', 'title', 'width', 'height', 'showTitle', 'isCloseable',
|
||||
'isPrivate', 'isLayoutMember', 'content', 'icon', 'events', 'onCreate', 'elContainer',
|
||||
'canHide', 'limit', 'extraClasses', 'canMaximise',
|
||||
];
|
||||
_.extend(this, _.pick(options, defaults));
|
||||
};
|
||||
|
||||
_.extend(pgAdmin.Browser.Panel.prototype, {
|
||||
name: '',
|
||||
title: '',
|
||||
width: 300,
|
||||
height: 600,
|
||||
showTitle: true,
|
||||
isCloseable: true,
|
||||
isPrivate: false,
|
||||
isLayoutMember: true,
|
||||
content: '',
|
||||
icon: '',
|
||||
panel: null,
|
||||
onCreate: null,
|
||||
elContainer: false,
|
||||
canMaximise: false,
|
||||
limit: null,
|
||||
extraClasses: null,
|
||||
load: function(docker, title) {
|
||||
let that = this;
|
||||
if (!that.panel) {
|
||||
docker.registerPanelType(that.name, {
|
||||
title: that.title,
|
||||
isPrivate: that.isPrivate,
|
||||
limit: that.limit,
|
||||
isLayoutMember: that.isLayoutMember,
|
||||
onCreate: function(myPanel) {
|
||||
myPanel.panelData = {
|
||||
pgAdminName: that.name,
|
||||
};
|
||||
myPanel.initSize(that.width, that.height);
|
||||
|
||||
if (!that.showTitle)
|
||||
myPanel.title(false);
|
||||
else {
|
||||
let title_elem = '<a href="#" tabindex="-1" class="panel-link-heading">' + (title || that.title) + '</a>';
|
||||
myPanel.title(title_elem);
|
||||
if (that.icon != '')
|
||||
myPanel.icon(that.icon);
|
||||
}
|
||||
|
||||
let container = document.createElement('div');
|
||||
container.setAttribute('class', 'pg-panel-content');
|
||||
container.innerHTML = that.content;
|
||||
|
||||
// Add extra classes
|
||||
if (!_.isNull('extraClasses')) {
|
||||
container.classList.add(that.extraClasses);
|
||||
}
|
||||
|
||||
myPanel.maximisable(!!that.canMaximise);
|
||||
myPanel.closeable(!!that.isCloseable);
|
||||
myPanel.layout().addItem(container);
|
||||
that.panel = myPanel;
|
||||
if (that.events && _.isObject(that.events)) {
|
||||
_.each(that.events, function(v, k) {
|
||||
if (v && _.isFunction(v)) {
|
||||
myPanel.on(k, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
_.each([
|
||||
wcDocker.EVENT.UPDATED, wcDocker.EVENT.VISIBILITY_CHANGED,
|
||||
wcDocker.EVENT.BEGIN_DOCK, wcDocker.EVENT.END_DOCK,
|
||||
wcDocker.EVENT.GAIN_FOCUS, wcDocker.EVENT.LOST_FOCUS,
|
||||
wcDocker.EVENT.CLOSED, wcDocker.EVENT.BUTTON,
|
||||
wcDocker.EVENT.ATTACHED, wcDocker.EVENT.DETACHED,
|
||||
wcDocker.EVENT.MOVE_STARTED, wcDocker.EVENT.MOVE_ENDED,
|
||||
wcDocker.EVENT.MOVED, wcDocker.EVENT.RESIZE_STARTED,
|
||||
wcDocker.EVENT.RESIZE_ENDED, wcDocker.EVENT.RESIZED,
|
||||
wcDocker.EVENT.SCROLLED,
|
||||
], function(ev) {
|
||||
myPanel.on(ev, that.eventFunc.bind(myPanel, ev));
|
||||
});
|
||||
|
||||
if (that.onCreate && _.isFunction(that.onCreate)) {
|
||||
that.onCreate.apply(that, [myPanel, container]);
|
||||
}
|
||||
|
||||
// Prevent browser from opening the drag file.
|
||||
// Using addEventListener to avoid conflict with jquery.drag
|
||||
['dragover', 'drop'].forEach((eventName)=>{
|
||||
container.addEventListener(eventName, function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
});
|
||||
|
||||
if (that.elContainer) {
|
||||
myPanel.pgElContainer = container;
|
||||
_.each([
|
||||
wcDocker.EVENT.RESIZED, wcDocker.EVENT.ATTACHED,
|
||||
wcDocker.EVENT.DETACHED, wcDocker.EVENT.VISIBILITY_CHANGED,
|
||||
], function(ev) {
|
||||
myPanel.on(ev, that.resizedContainer.bind(myPanel));
|
||||
});
|
||||
that.resizedContainer.apply(myPanel);
|
||||
}
|
||||
|
||||
if (myPanel._type == 'dashboard' || myPanel._type == 'processes') {
|
||||
getPanelView(
|
||||
pgBrowser.tree,
|
||||
container,
|
||||
pgBrowser,
|
||||
myPanel._type
|
||||
);
|
||||
}
|
||||
|
||||
// Re-render the dashboard panel when preference value 'show graph' gets changed.
|
||||
pgBrowser.onPreferencesChange('dashboards', function() {
|
||||
getPanelView(
|
||||
pgBrowser.tree,
|
||||
container,
|
||||
pgBrowser,
|
||||
myPanel._type
|
||||
);
|
||||
});
|
||||
|
||||
// Re-render the dashboard panel when preference value gets changed.
|
||||
pgBrowser.onPreferencesChange('graphs', function() {
|
||||
getPanelView(
|
||||
pgBrowser.tree,
|
||||
container,
|
||||
pgBrowser,
|
||||
myPanel._type
|
||||
);
|
||||
});
|
||||
|
||||
_.each([wcDocker.EVENT.CLOSED, wcDocker.EVENT.VISIBILITY_CHANGED],
|
||||
function(ev) {
|
||||
myPanel.on(ev, that.handleVisibility.bind(myPanel, ev));
|
||||
});
|
||||
|
||||
pgBrowser.Events.on('pgadmin-browser:tree:selected', () => {
|
||||
|
||||
if(myPanel.isVisible() && myPanel._type !== 'properties') {
|
||||
getPanelView(
|
||||
pgBrowser.tree,
|
||||
container,
|
||||
pgBrowser,
|
||||
myPanel._type
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
pgBrowser.Events.on('pgadmin:database:connected', () => {
|
||||
|
||||
if(myPanel.isVisible() && myPanel._type !== 'properties') {
|
||||
getPanelView(
|
||||
pgBrowser.tree,
|
||||
container,
|
||||
pgBrowser,
|
||||
myPanel._type
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
pgBrowser.Events.on('pgadmin-browser:tree:refreshing', () => {
|
||||
|
||||
if(myPanel.isVisible() && myPanel._type !== 'properties') {
|
||||
getPanelView(
|
||||
pgBrowser.tree,
|
||||
container,
|
||||
pgBrowser,
|
||||
myPanel._type
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
eventFunc: function(eventName) {
|
||||
let name = this.panelData.pgAdminName;
|
||||
try {
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin-browser:panel', eventName, this, arguments
|
||||
);
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin-browser:panel:' + eventName, this, arguments
|
||||
);
|
||||
|
||||
if (name) {
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin-browser:panel-' + name, eventName, this, arguments
|
||||
);
|
||||
pgBrowser.Events.trigger(
|
||||
'pgadmin-browser:panel-' + name + ':' + eventName, this, arguments
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e.stack || e);
|
||||
}
|
||||
},
|
||||
resizedContainer: function() {
|
||||
let p = this;
|
||||
|
||||
if (p.pgElContainer && !p.pgResizeTimeout) {
|
||||
if (!p.isVisible()) {
|
||||
clearTimeout(p.pgResizeTimeout);
|
||||
p.pgResizeTimeout = null;
|
||||
|
||||
return;
|
||||
}
|
||||
p.pgResizeTimeout = setTimeout(
|
||||
function() {
|
||||
let w = p.width(),
|
||||
elAttr = 'xs';
|
||||
p.pgResizeTimeout = null;
|
||||
|
||||
/** Calculations based on https://getbootstrap.com/docs/4.1/layout/grid/#grid-options **/
|
||||
if (w >= 480) {
|
||||
elAttr = 'sm';
|
||||
}
|
||||
if (w >= 768) {
|
||||
elAttr = 'md';
|
||||
}
|
||||
if (w >= 992) {
|
||||
elAttr = 'lg';
|
||||
}
|
||||
if (w >=1200) {
|
||||
elAttr = 'xl';
|
||||
}
|
||||
|
||||
p.pgElContainer.setAttribute('el', elAttr);
|
||||
},
|
||||
100
|
||||
);
|
||||
}
|
||||
},
|
||||
handleVisibility: function(eventName) {
|
||||
let selectedPanel = pgBrowser.docker.findPanels(this._type)[0];
|
||||
let isPanelVisible = selectedPanel.isVisible();
|
||||
let container = selectedPanel
|
||||
.layout()
|
||||
.scene()
|
||||
.find('.pg-panel-content');
|
||||
|
||||
if (isPanelVisible && ['dashboard', 'statistics', 'dependencies', 'dependents', 'sql', 'processes'].includes(selectedPanel._type) ) {
|
||||
if (eventName == 'panelVisibilityChanged') {
|
||||
getPanelView(
|
||||
pgBrowser.tree,
|
||||
container[0],
|
||||
pgBrowser,
|
||||
this._type
|
||||
);
|
||||
}
|
||||
}
|
||||
if (eventName == 'panelClosed' && selectedPanel._type == 'dashboard') {
|
||||
getPanelView(
|
||||
pgBrowser.tree,
|
||||
container[0],
|
||||
pgBrowser,
|
||||
this._type,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return pgAdmin.Browser.Panel;
|
||||
});
|
@ -1,148 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Theme from 'sources/Theme';
|
||||
import Dependencies from '../../../misc/dependencies/static/js/Dependencies';
|
||||
import Dependents from '../../../misc/dependents/static/js/Dependents';
|
||||
import Statistics from '../../../misc/statistics/static/js/Statistics';
|
||||
import SQL from '../../../misc/sql/static/js/SQL';
|
||||
import Dashboard from '../../../dashboard/static/js/Dashboard';
|
||||
import _ from 'lodash';
|
||||
import { CollectionNodeView } from '../../../misc/properties/CollectionNodeProperties';
|
||||
import Processes from '../../../misc/bgprocess/static/js/Processes';
|
||||
|
||||
|
||||
/* The entry point for rendering React based view in properties, called in node.js */
|
||||
export function getPanelView(
|
||||
tree,
|
||||
container,
|
||||
pgBrowser,
|
||||
panelType,
|
||||
panelVisible = true
|
||||
) {
|
||||
let item = !_.isNull(tree)? tree.selected(): null,
|
||||
nodeData, node, treeNodeInfo, preferences, graphPref, dashPref;
|
||||
|
||||
if (item){
|
||||
nodeData = tree.itemData(item);
|
||||
node = nodeData && pgBrowser.Nodes[nodeData._type];
|
||||
treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item);
|
||||
dashPref = pgBrowser.get_preferences_for_module('dashboards');
|
||||
graphPref = pgBrowser.get_preferences_for_module('graphs');
|
||||
preferences = _.merge(dashPref, graphPref);
|
||||
|
||||
}
|
||||
if (panelType == 'dashboard') {
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<Dashboard
|
||||
treeNodeInfo={treeNodeInfo}
|
||||
pgBrowser={pgBrowser}
|
||||
nodeData={nodeData}
|
||||
node={node}
|
||||
item={item}
|
||||
preferences={preferences}
|
||||
did={((!_.isUndefined(treeNodeInfo)) && (!_.isUndefined(treeNodeInfo['database']))) ? treeNodeInfo['database']._id: 0}
|
||||
sid={!_.isUndefined(treeNodeInfo) && !_.isUndefined(treeNodeInfo['server']) ? treeNodeInfo['server']._id : ''}
|
||||
serverConnected={!_.isUndefined(treeNodeInfo) && !_.isUndefined(treeNodeInfo['server']) ? treeNodeInfo.server.connected: false}
|
||||
dbConnected={!_.isUndefined(treeNodeInfo) && !_.isUndefined(treeNodeInfo['database']) ? treeNodeInfo.database.connected: false}
|
||||
panelVisible={panelVisible}
|
||||
/>
|
||||
</Theme>,
|
||||
container
|
||||
);
|
||||
}
|
||||
if (panelType == 'statistics') {
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<Statistics
|
||||
treeNodeInfo={treeNodeInfo}
|
||||
pgBrowser={pgBrowser}
|
||||
nodeData={nodeData}
|
||||
node={node}
|
||||
item={item}
|
||||
/>
|
||||
</Theme>,
|
||||
container
|
||||
);
|
||||
}
|
||||
if (panelType == 'properties' && nodeData?.is_collection) {
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<CollectionNodeView
|
||||
treeNodeInfo={treeNodeInfo}
|
||||
item={item}
|
||||
itemNodeData={nodeData}
|
||||
node={node}
|
||||
pgBrowser={pgBrowser}
|
||||
/>
|
||||
</Theme>,
|
||||
container
|
||||
);
|
||||
}
|
||||
if (panelType == 'dependencies') {
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<Dependencies
|
||||
treeNodeInfo={treeNodeInfo}
|
||||
pgBrowser={pgBrowser}
|
||||
nodeData={nodeData}
|
||||
node={node}
|
||||
item={item}
|
||||
/>
|
||||
</Theme>,
|
||||
container
|
||||
);
|
||||
}
|
||||
if (panelType == 'dependents') {
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<Dependents
|
||||
treeNodeInfo={treeNodeInfo}
|
||||
pgBrowser={pgBrowser}
|
||||
nodeData={nodeData}
|
||||
node={node}
|
||||
item={item}
|
||||
/>
|
||||
</Theme>,
|
||||
container
|
||||
);
|
||||
}
|
||||
if (panelType == 'sql') {
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<SQL
|
||||
treeNodeInfo={treeNodeInfo}
|
||||
pgBrowser={pgBrowser}
|
||||
nodeData={nodeData}
|
||||
node={node}
|
||||
item={item}
|
||||
did={((!_.isUndefined(treeNodeInfo)) && (!_.isUndefined(treeNodeInfo['database']))) ? treeNodeInfo['database']._id: 0}
|
||||
dbConnected={!_.isUndefined(treeNodeInfo) && !_.isUndefined(treeNodeInfo['database']) ? treeNodeInfo.database.connected: false}
|
||||
/>
|
||||
</Theme>,
|
||||
container
|
||||
);
|
||||
}
|
||||
if (panelType == 'processes') {
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<Processes />
|
||||
</Theme>,
|
||||
container
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* When switching from normal node to collection node, clean up the React mounted DOM */
|
||||
export function removePanelView(container) {
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import url_for from 'sources/url_for';
|
||||
import Notify from '../../../static/js/helpers/Notifier';
|
||||
import { shortcutToString } from '../../../static/js/components/ShortcutTitle';
|
||||
import gettext from 'sources/gettext';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
|
||||
|
||||
const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||
|
||||
/* Add cache related methods and properties */
|
||||
_.extend(pgBrowser, {
|
||||
/* This will hold preference data (Works as a cache object)
|
||||
* Here node will be a key and it's preference data will be value
|
||||
*/
|
||||
preferences_cache: [],
|
||||
|
||||
/* This will be used by poller of new tabs/windows to check
|
||||
* if preference cache is updated in parent/window.opener.
|
||||
*/
|
||||
prefcache_version: 0,
|
||||
|
||||
/* Generate a unique version number */
|
||||
generate_preference_version: function() {
|
||||
return (new Date()).getTime();
|
||||
},
|
||||
|
||||
preference_version: function(version) {
|
||||
if(version) {
|
||||
this.prefcache_version = version;
|
||||
}
|
||||
else {
|
||||
return this.prefcache_version;
|
||||
}
|
||||
},
|
||||
|
||||
/* Get cached preference */
|
||||
get_preference: function(module, preference){
|
||||
const self = this;
|
||||
|
||||
return _.find(
|
||||
self.preferences_cache, {'module': module, 'name': preference}
|
||||
);
|
||||
},
|
||||
|
||||
/* Get all the preferences of a module */
|
||||
get_preferences_for_module: function(module) {
|
||||
let self = this;
|
||||
let preferences = {};
|
||||
_.forEach(
|
||||
_.filter(self.preferences_cache, {'module': module}),
|
||||
(preference) => {
|
||||
preferences[preference.name] = preference.value;
|
||||
}
|
||||
);
|
||||
if(Object.keys(preferences).length > 0) {
|
||||
return preferences;
|
||||
}
|
||||
},
|
||||
|
||||
/* Get preference of an id, id is numeric */
|
||||
get_preference_for_id : function(id) {
|
||||
let self = this;
|
||||
/* find returns undefined if not found */
|
||||
return _.find(self.preferences_cache, {'id': id});
|
||||
},
|
||||
|
||||
// Get and cache the preferences
|
||||
cache_preferences: function (modulesChanged) {
|
||||
let self = this;
|
||||
|
||||
setTimeout(function() {
|
||||
getApiInstance().get(url_for('preferences.get_all'))
|
||||
.then(({data: res})=>{
|
||||
self.preferences_cache = res;
|
||||
self.preference_version(self.generate_preference_version());
|
||||
|
||||
pgBrowser.keyboardNavigation.init();
|
||||
|
||||
// Initialize Tree saving/reloading
|
||||
pgBrowser.browserTreeState.init();
|
||||
|
||||
/* Once the cache is loaded after changing the preferences,
|
||||
* notify the modules of the change
|
||||
*/
|
||||
if(modulesChanged) {
|
||||
if(typeof modulesChanged === 'string'){
|
||||
pgBrowser.Events.trigger('prefchange:'+modulesChanged);
|
||||
} else {
|
||||
_.each(modulesChanged, (val, key)=> {
|
||||
pgBrowser.Events.trigger('prefchange:'+key);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
Notify.pgRespErrorNotify(error);
|
||||
});
|
||||
}, 500);
|
||||
},
|
||||
|
||||
triggerPreferencesChange: function(moduleChanged) {
|
||||
pgBrowser.Events.trigger('prefchange:'+moduleChanged);
|
||||
},
|
||||
|
||||
reflectPreferences: function(module) {
|
||||
let obj = this;
|
||||
//browser preference
|
||||
if(module === 'browser') {
|
||||
let browserPreferences = obj.get_preferences_for_module('browser');
|
||||
let buttonList = obj?.panels?.browser?.panel?._buttonList;
|
||||
buttonList.forEach(btn => {
|
||||
let key = null;
|
||||
switch(btn.name) {
|
||||
case gettext('Query Tool'):
|
||||
key = shortcutToString(browserPreferences.sub_menu_query_tool,null,true);
|
||||
obj?.panels?.browser?.panel?.updateButton(gettext('Query Tool'), {key});
|
||||
break;
|
||||
case gettext('View Data'):
|
||||
key = shortcutToString(browserPreferences.sub_menu_view_data,null,true);
|
||||
obj?.panels?.browser?.panel?.updateButton(gettext('View Data'), {key});
|
||||
break;
|
||||
case gettext('Search objects'):
|
||||
key = shortcutToString(browserPreferences.sub_menu_search_objects,null,true);
|
||||
obj?.panels?.browser?.panel?.updateButton(gettext('Search objects'), {key});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onPreferencesChange: function(module, eventHandler) {
|
||||
pgBrowser.Events?.on('prefchange:'+module, function(event) {
|
||||
eventHandler(event);
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export {pgBrowser};
|
@ -1,44 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// Allow us to render IFrame using React
|
||||
// Here we will add the event listener on Iframe load event
|
||||
export class Iframe extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
id: PropTypes.string.isRequired,
|
||||
srcURL: PropTypes.string.isRequired,
|
||||
onLoad: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render () {
|
||||
const iframeStyle = {
|
||||
border: '0',
|
||||
display: 'block',
|
||||
position:'absolute',
|
||||
opacity:'0',
|
||||
};
|
||||
const {id, srcURL, onLoad} = this.props;
|
||||
|
||||
return (
|
||||
<iframe
|
||||
id={id}
|
||||
src={srcURL}
|
||||
onLoad={onLoad}
|
||||
width={'20'}
|
||||
height={'20'}
|
||||
style={iframeStyle}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import _ from 'lodash';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
let _toolbarButtons = {};
|
||||
let _browserPanel = null;
|
||||
|
||||
// Default Tool Bar Buttons.
|
||||
let _defaultToolBarButtons = [
|
||||
{
|
||||
label: gettext('Search objects'),
|
||||
ariaLabel: gettext('Search objects'),
|
||||
btnClass: 'fa fa-search',
|
||||
text: '',
|
||||
toggled: false,
|
||||
toggleClass: '',
|
||||
parentClass: 'pg-toolbar-btn btn-primary-icon',
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
label: gettext('Filtered Rows'),
|
||||
ariaLabel: gettext('Filtered Rows'),
|
||||
btnClass: 'pg-font-icon icon-row_filter',
|
||||
text: '',
|
||||
toggled: false,
|
||||
toggleClass: '',
|
||||
parentClass: 'pg-toolbar-btn btn-primary-icon',
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
label: gettext('View Data'),
|
||||
ariaLabel: gettext('View Data'),
|
||||
btnClass: 'pg-font-icon sql-icon-lg icon-view_data',
|
||||
text: '',
|
||||
toggled: false,
|
||||
toggleClass: '',
|
||||
parentClass: 'pg-toolbar-btn btn-primary-icon',
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
label: gettext('Query Tool'),
|
||||
ariaLabel: gettext('Query Tool'),
|
||||
btnClass: 'pg-font-icon icon-query_tool',
|
||||
text: '',
|
||||
toggled: false,
|
||||
toggleClass: '',
|
||||
parentClass: 'pg-toolbar-btn btn-primary-icon',
|
||||
enabled: false,
|
||||
}
|
||||
];
|
||||
|
||||
if(pgAdmin['enable_psql']) {
|
||||
_defaultToolBarButtons.unshift({
|
||||
label: gettext('PSQL Tool'),
|
||||
ariaLabel: gettext('PSQL Tool'),
|
||||
btnClass: 'fas fa-terminal',
|
||||
text: '',
|
||||
toggled: false,
|
||||
toggleClass: '',
|
||||
parentClass: 'pg-toolbar-btn btn-primary-icon pg-toolbar-psql',
|
||||
enabled: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Place holder for non default tool bar buttons.
|
||||
let _otherToolbarButtons = [];
|
||||
|
||||
// This function is used to add button into the browser panel.
|
||||
function registerToolBarButton(btn) {
|
||||
/* Sometimes the panel onCreate is called two times.
|
||||
* Add buttons if not present in the panel also.
|
||||
*/
|
||||
if (!(btn.label in _toolbarButtons)
|
||||
|| (_.findIndex(_browserPanel._buttonList,{name:btn.label}) < 0)) {
|
||||
_browserPanel.addButton(
|
||||
btn.label, btn.btnClass, btn.text, btn.label, btn.toggled,
|
||||
btn.toggleClass, btn.parentClass, btn.enabled, btn.ariaLabel
|
||||
);
|
||||
|
||||
_toolbarButtons[btn.label] = btn;
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used to add tool bar button and
|
||||
// listen on the button event.
|
||||
export function initializeToolbar(panel, wcDocker) {
|
||||
_browserPanel = panel;
|
||||
|
||||
// Iterate through default tool bar buttons and add them into the
|
||||
// browser panel.
|
||||
_.each(_defaultToolBarButtons, (btn) => {
|
||||
registerToolBarButton(btn);
|
||||
});
|
||||
|
||||
// Iterate through other tool bar buttons and add them into the
|
||||
// browser panel.
|
||||
_.each(_otherToolbarButtons, (btn) => {
|
||||
registerToolBarButton(btn);
|
||||
});
|
||||
|
||||
// Listen on button click event.
|
||||
panel.on(wcDocker.EVENT.BUTTON, function(data) {
|
||||
if ('name' in data && data.name === gettext('Query Tool'))
|
||||
pgAdmin.Tools.SQLEditor.showQueryTool('', pgAdmin.Browser.tree.selected());
|
||||
else if ('name' in data && data.name === gettext('View Data'))
|
||||
pgAdmin.Tools.SQLEditor.showViewData({mnuid: 3}, pgAdmin.Browser.tree.selected());
|
||||
else if ('name' in data && data.name === gettext('Filtered Rows'))
|
||||
pgAdmin.Tools.SQLEditor.showFilteredRow({mnuid: 4}, pgAdmin.Browser.tree.selected());
|
||||
else if ('name' in data && data.name === gettext('Search objects'))
|
||||
pgAdmin.Tools.SearchObjects.show_search_objects('', pgAdmin.Browser.tree.selected());
|
||||
else if ('name' in data && data.name === gettext('PSQL Tool')){
|
||||
let input = {},
|
||||
t = pgAdmin.Browser.tree,
|
||||
i = input.item || t.selected(),
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
pgAdmin.Browser.psql.psql_tool(d, i, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// This function is used to enable/disable the specific button
|
||||
// based on their label.
|
||||
export function enable(label, enable_btn) {
|
||||
if (label in _toolbarButtons) {
|
||||
_browserPanel.buttonEnable(label, enable_btn);
|
||||
} else {
|
||||
console.warn('Developer warning: No tool button found with label: ' + label);
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
#myInput {
|
||||
box-sizing: border-box;
|
||||
background-position: 14px 12px;
|
||||
background-repeat: no-repeat;
|
||||
font-size: 16px;
|
||||
padding: 14px 20px 12px 45px;
|
||||
border: none;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
#myInput:focus {outline: 3px solid #ddd;}
|
||||
|
||||
.custom-dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.custom-dropdown-content {
|
||||
background-color: $color-bg;
|
||||
min-width: 376px;
|
||||
overflow: auto;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.custom-dropdown-content a {
|
||||
color: $dropdown-link-color;
|
||||
padding: 6px 12px 6px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.custom-dropdown-content a:hover {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
#myDropdown a:hover {background-color: $dropdown-link-hover-bg; color:$color-danger-fg !important;}
|
||||
|
||||
.search_icon{
|
||||
color: $white;
|
||||
cursor: pointer;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.hidden { display:none; }
|
||||
|
||||
.visible { display:block; }
|
||||
|
||||
.menu-groups, .help-groups{
|
||||
background-color: $color-gray-light;
|
||||
padding: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.menu-groups .fa, .fas{
|
||||
font-weight:normal !important;
|
||||
}
|
||||
|
||||
.help-groups .fa, .fas{
|
||||
font-weight:600 !important;
|
||||
}
|
||||
|
||||
.pad-12{
|
||||
padding:12px;
|
||||
}
|
||||
|
||||
.no-results{
|
||||
font-size: 14px;
|
||||
color: #697986;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-padding{
|
||||
padding:0 !important;
|
||||
}
|
||||
|
||||
.menu-groups-a{
|
||||
display:flex !important;
|
||||
flex-direction:column;
|
||||
padding: 6px 16px;
|
||||
color: $dropdown-link-color;
|
||||
}
|
||||
|
||||
.menu-groups-a span{
|
||||
font-size: 0.9em;
|
||||
font-weight: 100;
|
||||
color: $quick-search-span-text;
|
||||
}
|
||||
|
||||
#myDropdown a:hover span{
|
||||
color: $color-danger-fg !important;
|
||||
}
|
||||
|
||||
.search-icon{
|
||||
background: $loader-icon-small center center no-repeat;
|
||||
margin: auto !important;
|
||||
height: 22px !important;
|
||||
width: 130px !important;
|
||||
background-position: left !important;
|
||||
font-size: 14px;
|
||||
color: $dropdown-link-color;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.help_loading_icon{
|
||||
height: 16px;
|
||||
}
|
||||
#myDropdown ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.border-right-search-icon{
|
||||
border-right: 2px solid #fff;
|
||||
}
|
||||
|
||||
.help_submenu{
|
||||
left: 100%;
|
||||
width: 20rem;
|
||||
top:-0.4rem;
|
||||
}
|
||||
|
||||
.help_menu{
|
||||
min-width:300px;
|
||||
}
|
||||
|
||||
.dropdown-item-input{
|
||||
padding:4px;
|
||||
}
|
||||
|
||||
.menu-groups-a span:focus{
|
||||
color:$color-primary-fg;
|
||||
}
|
||||
|
||||
.dropdown-item:hover, .dropdown-item:focus span{
|
||||
color: $color-danger-fg !important;
|
||||
}
|
||||
|
||||
.quick-search-tooltip{
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
margin-top: -2.1em;
|
||||
font-size: 16px;
|
||||
cursor:pointer;
|
||||
color: $quick-search-info-icon;
|
||||
}
|
||||
|
||||
#myDropdown .dropdown-divider{
|
||||
height: auto;
|
||||
border-top: 0;
|
||||
}
|
@ -92,14 +92,6 @@ require.onResourceLoad = function (context, map, depMaps) {
|
||||
<div class="row"><div class="col-12 pg-sp-text">{{ _('Loading {0} v{1}...').format(config.APP_NAME, config.APP_VERSION) }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
{% if current_app.PGADMIN_RUNTIME | string() == 'False' %}
|
||||
<div id="main-menu-container"></div>
|
||||
<div id="dockerContainer" class="pg-docker"></div>
|
||||
{% else %}
|
||||
<div id="dockerContainer" class="pg-docker pg-docker-native"></div>
|
||||
{% endif %}
|
||||
<div id="object-breadcrumbs"></div>
|
||||
|
||||
{% include 'browser/messages.html' %}
|
||||
<div id="root" style="height: 100%"></div>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,18 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
define('pgadmin.browser.constants', [], function() {
|
||||
return {
|
||||
'INTERNAL': '{{ INTERNAL }}',
|
||||
'LDAP': '{{ LDAP }}',
|
||||
'KERBEROS': '{{ KERBEROS }}',
|
||||
'OAUTH2': '{{ OAUTH2 }}'
|
||||
}
|
||||
});
|
@ -20,7 +20,7 @@
|
||||
{% endif %}label: "{{ item.label }}", applies: ["{{ key.lower() }}"],
|
||||
priority: {{ item.priority }},
|
||||
enable: "{{ item.enable }}",
|
||||
{% if item.checked is defined %}checked: {% if (item.checked or item.name == 'mnu_lock_'+current_ui_lock) %}true{% else %}false{% endif %},
|
||||
{% if item.checked is defined %}checked: {% if item.checked %}true{% else %}false{% endif %},
|
||||
{% endif %}
|
||||
{% if item.below is defined %}below: {% if item.below %}true{% else %}false{% endif %},
|
||||
{% endif %}
|
||||
@ -41,9 +41,6 @@ define('pgadmin.browser.utils',
|
||||
|
||||
let pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||
|
||||
/* Add hooked-in panels by extensions */
|
||||
pgBrowser['panels_items'] = '{{ current_app.panels|tojson }}';
|
||||
|
||||
pgBrowser['MainMenus'] = [];
|
||||
|
||||
pgAdmin['csrf_token_header'] = '{{ current_app.config.get('WTF_CSRF_HEADERS')[0] }}';
|
||||
|
@ -44,22 +44,6 @@ class DashboardModule(PgAdminModule):
|
||||
stylesheets = []
|
||||
return stylesheets
|
||||
|
||||
def get_panels(self):
|
||||
return [
|
||||
Panel(
|
||||
name='dashboard',
|
||||
priority=1,
|
||||
title=gettext('Dashboard'),
|
||||
icon='',
|
||||
content='',
|
||||
is_closeable=True,
|
||||
is_private=False,
|
||||
limit=1,
|
||||
is_iframe=False,
|
||||
can_hide=True
|
||||
).__dict__
|
||||
]
|
||||
|
||||
def register_preferences(self):
|
||||
"""
|
||||
register_preferences
|
||||
|
74
web/pgadmin/dashboard/static/js/ChartContainer.jsx
Normal file
74
web/pgadmin/dashboard/static/js/ChartContainer.jsx
Normal file
@ -0,0 +1,74 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Box, Card, CardContent, CardHeader, makeStyles } from '@material-ui/core';
|
||||
|
||||
import EmptyPanelMessage from '../../../static/js/components/EmptyPanelMessage';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
chartCard: {
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
},
|
||||
chartCardContent: {
|
||||
padding: '0.25rem 0.5rem',
|
||||
height: '165px',
|
||||
display: 'flex',
|
||||
},
|
||||
chartLegend: {
|
||||
marginLeft: 'auto',
|
||||
'& > div': {
|
||||
display: 'flex',
|
||||
fontWeight: 'normal',
|
||||
|
||||
'& .legend-value': {
|
||||
marginLeft: '4px',
|
||||
'& .legend-label': {
|
||||
marginLeft: '4px',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
export default function ChartContainer(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Card className={classes.chartCard} elevation={0} data-testid="chart-container">
|
||||
<CardHeader title={<Box display="flex" justifyContent="space-between">
|
||||
<div id={props.id}>{props.title}</div>
|
||||
<div className={classes.chartLegend}>
|
||||
<div className="d-flex">
|
||||
{props.datasets?.map((datum)=>(
|
||||
<div className="legend-value" key={datum.label}>
|
||||
<span style={{backgroundColor: datum.borderColor}}> </span>
|
||||
<span className="legend-label">{datum.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Box>} />
|
||||
<CardContent className={classes.chartCardContent}>
|
||||
{!props.errorMsg && !props.isTest && props.children}
|
||||
{props.errorMsg && <EmptyPanelMessage text={props.errorMsg}/>}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
ChartContainer.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
datasets: PropTypes.array.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
errorMsg: PropTypes.string,
|
||||
isTest: PropTypes.bool
|
||||
};
|
@ -35,15 +35,6 @@ export default class ChartsDOM {
|
||||
this.render();
|
||||
}
|
||||
|
||||
reflectPreferences(preferences) {
|
||||
this.preferences = preferences;
|
||||
if(preferences.show_graphs) {
|
||||
this.render();
|
||||
} else {
|
||||
this.unmount();
|
||||
}
|
||||
}
|
||||
|
||||
setPageVisible(visible) {
|
||||
this.pageVisible = visible;
|
||||
this.render();
|
||||
|
@ -6,7 +6,6 @@
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
// eslint-disable-next-line react/display-name
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
@ -16,8 +15,7 @@ import { InputCheckbox } from '../../../static/js/components/FormComponents';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import url_for from 'sources/url_for';
|
||||
import Graphs from './Graphs';
|
||||
import Notify from '../../../static/js/helpers/Notifier';
|
||||
import { Box, Card, CardContent, CardHeader, Tab, Tabs } from '@material-ui/core';
|
||||
import { Box, Tab, Tabs } from '@material-ui/core';
|
||||
import { PgIconButton } from '../../../static/js/components/Buttons';
|
||||
import CancelIcon from '@material-ui/icons/Cancel';
|
||||
import StopSharpIcon from '@material-ui/icons/StopSharp';
|
||||
@ -33,6 +31,11 @@ import Summary from './SystemStats/Summary';
|
||||
import CPU from './SystemStats/CPU';
|
||||
import Memory from './SystemStats/Memory';
|
||||
import Storage from './SystemStats/Storage';
|
||||
import withStandardTabInfo from '../../../static/js/helpers/withStandardTabInfo';
|
||||
import { BROWSER_PANELS } from '../../../browser/static/js/constants';
|
||||
import { usePgAdmin } from '../../../static/js/BrowserComponent';
|
||||
import usePreferences from '../../../preferences/static/js/store';
|
||||
import ErrorBoundary from '../../../static/js/helpers/ErrorBoundary';
|
||||
|
||||
function parseData(data) {
|
||||
let res = [];
|
||||
@ -45,13 +48,11 @@ function parseData(data) {
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
height: '100%',
|
||||
background: theme.palette.grey[400],
|
||||
width: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
overflow: 'auto',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
},
|
||||
fixedSizeList: {
|
||||
overflowX: 'hidden !important',
|
||||
@ -91,12 +92,20 @@ const useStyles = makeStyles((theme) => ({
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
panelContent: {
|
||||
...theme.mixins.panelBorder,
|
||||
...theme.mixins.panelBorder.all,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden !important',
|
||||
height: '100%',
|
||||
minHeight: '400px'
|
||||
width: '100%',
|
||||
minHeight: '400px',
|
||||
padding: '8px'
|
||||
},
|
||||
mainTabs: {
|
||||
...theme.mixins.panelBorder.all,
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
arrowButton: {
|
||||
fontSize: '2rem !important',
|
||||
@ -139,16 +148,8 @@ const useStyles = makeStyles((theme) => ({
|
||||
}
|
||||
}));
|
||||
|
||||
/* eslint-disable react/display-name */
|
||||
export default function Dashboard({
|
||||
nodeData,
|
||||
node,
|
||||
item,
|
||||
pgBrowser,
|
||||
preferences,
|
||||
sid,
|
||||
did,
|
||||
treeNodeInfo,
|
||||
function Dashboard({
|
||||
nodeItem, nodeData, node, treeNodeInfo,
|
||||
...props
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
@ -169,6 +170,16 @@ export default function Dashboard({
|
||||
const systemStatsTabChanged = (e, tabVal) => {
|
||||
setSystemStatsTabVal(tabVal);
|
||||
};
|
||||
const pgAdmin = usePgAdmin();
|
||||
const did = treeNodeInfo?.database?._id ?? 0;
|
||||
const sid = treeNodeInfo?.server?._id ?? 0;
|
||||
const dbConnected = treeNodeInfo?.database?.connected ?? false;
|
||||
const serverConnected = treeNodeInfo?.server?.connected ?? false;
|
||||
const prefStore = usePreferences();
|
||||
const preferences = _.merge(
|
||||
usePreferences().getPreferencesForModule('dashboards'),
|
||||
usePreferences().getPreferencesForModule('graphs')
|
||||
);
|
||||
|
||||
if (!did) {
|
||||
tabs.push(gettext('Configuration'));
|
||||
@ -268,7 +279,7 @@ export default function Dashboard({
|
||||
)
|
||||
return;
|
||||
let url = action_url + '/' + row.values.pid;
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
title,
|
||||
txtConfirm,
|
||||
function () {
|
||||
@ -276,14 +287,14 @@ export default function Dashboard({
|
||||
.delete(url)
|
||||
.then(function (res) {
|
||||
if (res.data == gettext('Success')) {
|
||||
Notify.success(txtSuccess);
|
||||
pgAdmin.Browser.notifier.success(txtSuccess);
|
||||
setRefresh(!refresh);
|
||||
} else {
|
||||
Notify.error(txtError);
|
||||
pgAdmin.Browser.notifier.error(txtError);
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Failed to retrieve data from the server.'),
|
||||
error.message
|
||||
);
|
||||
@ -335,7 +346,7 @@ export default function Dashboard({
|
||||
if (!canTakeAction(row, 'cancel'))
|
||||
return;
|
||||
let url = action_url + '/' + row.values.pid;
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
title,
|
||||
txtConfirm,
|
||||
function () {
|
||||
@ -343,16 +354,16 @@ export default function Dashboard({
|
||||
.delete(url)
|
||||
.then(function (res) {
|
||||
if (res.data == gettext('Success')) {
|
||||
Notify.success(txtSuccess);
|
||||
pgAdmin.Browser.notifier.success(txtSuccess);
|
||||
setRefresh(!refresh);
|
||||
} else {
|
||||
Notify.error(txtError);
|
||||
pgAdmin.Browser.notifier.error(txtError);
|
||||
setRefresh(!refresh);
|
||||
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Failed to retrieve data from the server.'),
|
||||
error.message
|
||||
);
|
||||
@ -711,7 +722,7 @@ export default function Dashboard({
|
||||
'You cannot terminate background worker processes.'
|
||||
);
|
||||
}
|
||||
Notify.info(txtMessage);
|
||||
pgAdmin.Browser.notifier.info(txtMessage);
|
||||
return false;
|
||||
// If it is the last active connection on maintenance db then error out
|
||||
} else if (
|
||||
@ -727,11 +738,11 @@ export default function Dashboard({
|
||||
'You are not allowed to terminate the main active session.'
|
||||
);
|
||||
}
|
||||
Notify.error(txtMessage);
|
||||
pgAdmin.Browser.notifier.error(txtMessage);
|
||||
return false;
|
||||
} else if (is_cancel_session && row.original.state == 'idle') {
|
||||
// If this session is already idle then do nothing
|
||||
Notify.info(gettext('The session is already in idle state.'));
|
||||
pgAdmin.Browser.notifier.info(gettext('The session is already in idle state.'));
|
||||
return false;
|
||||
} else if (can_signal_backend) {
|
||||
// user with membership of 'pg_signal_backend' can terminate the session of non admin user.
|
||||
@ -753,7 +764,7 @@ export default function Dashboard({
|
||||
'Superuser privileges are required to terminate another users query.'
|
||||
);
|
||||
}
|
||||
Notify.error(txtMessage);
|
||||
pgAdmin.Browser.notifier.error(txtMessage);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@ -773,7 +784,7 @@ export default function Dashboard({
|
||||
setTabVal(0);
|
||||
}
|
||||
|
||||
if (sid && props.serverConnected) {
|
||||
if (sid && serverConnected) {
|
||||
if (tabVal === 0) {
|
||||
url = url_for('dashboard.activity');
|
||||
} else if (tabVal === 1) {
|
||||
@ -785,11 +796,11 @@ export default function Dashboard({
|
||||
}
|
||||
|
||||
message = gettext('Loading dashboard...');
|
||||
if (did && !props.dbConnected) return;
|
||||
if (did && !dbConnected) return;
|
||||
if (did) url += sid + '/' + did;
|
||||
else url += sid;
|
||||
|
||||
if (did && !props.dbConnected) return;
|
||||
if (did && !dbConnected) return;
|
||||
if (did && did > 0) ssExtensionCheckUrl += '/' + sid + '/' + did;
|
||||
else ssExtensionCheckUrl += '/' + sid;
|
||||
|
||||
@ -804,7 +815,7 @@ export default function Dashboard({
|
||||
setdashData(parseData(res.data));
|
||||
})
|
||||
.catch((error) => {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Failed to retrieve data from the server.'),
|
||||
_.isUndefined(error.response) ? error.message : error.response.data.errormsg
|
||||
);
|
||||
@ -842,7 +853,7 @@ export default function Dashboard({
|
||||
if (message != '') {
|
||||
setMsg(message);
|
||||
}
|
||||
}, [nodeData, tabVal, did, preferences, refresh, props.dbConnected, mainTabVal]);
|
||||
}, [nodeData, tabVal, treeNodeInfo, prefStore, refresh, mainTabVal]);
|
||||
|
||||
const filteredDashData = useMemo(()=>{
|
||||
if (tabVal == 0 && activeOnly) {
|
||||
@ -872,7 +883,7 @@ export default function Dashboard({
|
||||
|
||||
const showDefaultContents = () => {
|
||||
return (
|
||||
sid && !props.serverConnected ? (
|
||||
sid && !serverConnected ? (
|
||||
<Box className={classes.dashboardPanel}>
|
||||
<div className={classes.emptyPanel}>
|
||||
<EmptyPanelMessage text={msg}/>
|
||||
@ -880,10 +891,10 @@ export default function Dashboard({
|
||||
</Box>
|
||||
) : (
|
||||
<WelcomeDashboard
|
||||
pgBrowser={pgBrowser}
|
||||
pgBrowser={pgAdmin.Browser}
|
||||
node={node}
|
||||
itemData={nodeData}
|
||||
item={item}
|
||||
item={nodeItem}
|
||||
sid={sid}
|
||||
did={did}
|
||||
/>
|
||||
@ -912,150 +923,148 @@ export default function Dashboard({
|
||||
|
||||
return (
|
||||
<>
|
||||
{sid && props.serverConnected ? (
|
||||
{sid && serverConnected ? (
|
||||
<Box className={classes.dashboardPanel}>
|
||||
<Box className={classes.emptyPanel}>
|
||||
<Box className={classes.panelContent}>
|
||||
<Box height="100%" display="flex" flexDirection="column">
|
||||
<Box>
|
||||
<Tabs
|
||||
value={mainTabVal}
|
||||
onChange={mainTabChanged}
|
||||
>
|
||||
{mainTabs.map((tabValue) => {
|
||||
return <Tab key={tabValue} label={tabValue} />;
|
||||
})}
|
||||
</Tabs>
|
||||
</Box>
|
||||
{/* General Statistics */}
|
||||
<TabPanel value={mainTabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
{!_.isUndefined(preferences) && preferences.show_graphs && (
|
||||
<Graphs
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.panelVisible}
|
||||
></Graphs>
|
||||
)}
|
||||
{!_.isUndefined(preferences) && preferences.show_activity && (
|
||||
<Box className={classes.panelContent}>
|
||||
<Box
|
||||
className={classes.cardHeader}
|
||||
title={props.dbConnected ? gettext('Database activity') : gettext('Server activity')}
|
||||
>
|
||||
{props.dbConnected ? gettext('Database activity') : gettext('Server activity')}{' '}
|
||||
</Box>
|
||||
<Box height="100%" display="flex" flexDirection="column">
|
||||
<Box>
|
||||
<Tabs
|
||||
value={tabVal}
|
||||
onChange={tabChanged}
|
||||
>
|
||||
{tabs.map((tabValue) => {
|
||||
return <Tab key={tabValue} label={tabValue} />;
|
||||
})}
|
||||
<RefreshButton/>
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={tabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
CustomHeader={CustomActiveOnlyHeader}
|
||||
columns={activityColumns}
|
||||
data={filteredDashData}
|
||||
schema={schemaDict}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
columns={databaseLocksColumns}
|
||||
data={dashData}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={2} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
columns={databasePreparedColumns}
|
||||
data={dashData}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={3} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
columns={serverConfigColumns}
|
||||
data={dashData}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</TabPanel>
|
||||
{/* System Statistics */}
|
||||
<TabPanel value={mainTabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<Box height="100%" display="flex" flexDirection="column">
|
||||
{ssMsg === 'installed' && did === ldid ?
|
||||
<>
|
||||
<Box>
|
||||
<Tabs
|
||||
value={systemStatsTabVal}
|
||||
onChange={systemStatsTabChanged}
|
||||
>
|
||||
{systemStatsTabs.map((tabValue) => {
|
||||
return <Tab key={tabValue} label={tabValue} />;
|
||||
})}
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={systemStatsTabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
<Summary
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.panelVisible}
|
||||
serverConnected={props.serverConnected}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={systemStatsTabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<CPU
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.panelVisible}
|
||||
serverConnected={props.serverConnected}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={systemStatsTabVal} index={2} classNameRoot={classes.tabPanel}>
|
||||
<Memory
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.panelVisible}
|
||||
serverConnected={props.serverConnected}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={systemStatsTabVal} index={3} classNameRoot={classes.tabPanel}>
|
||||
<Storage
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.panelVisible}
|
||||
serverConnected={props.serverConnected}
|
||||
systemStatsTabVal={systemStatsTabVal}
|
||||
/>
|
||||
</TabPanel>
|
||||
</> :
|
||||
<div className={classes.emptyPanel}>
|
||||
<EmptyPanelMessage text={ssMsg}/>
|
||||
</div>
|
||||
}
|
||||
</Box>
|
||||
</TabPanel>
|
||||
<Box className={classes.panelContent}>
|
||||
<Box className={classes.mainTabs}>
|
||||
<Box>
|
||||
<Tabs
|
||||
value={mainTabVal}
|
||||
onChange={mainTabChanged}
|
||||
>
|
||||
{mainTabs.map((tabValue) => {
|
||||
return <Tab key={tabValue} label={tabValue} />;
|
||||
})}
|
||||
</Tabs>
|
||||
</Box>
|
||||
{/* General Statistics */}
|
||||
<TabPanel value={mainTabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
{!_.isUndefined(preferences) && preferences.show_graphs && (
|
||||
<Graphs
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.isActive}
|
||||
></Graphs>
|
||||
)}
|
||||
{!_.isUndefined(preferences) && preferences.show_activity && (
|
||||
<Box className={classes.panelContent}>
|
||||
<Box
|
||||
className={classes.cardHeader}
|
||||
title={dbConnected ? gettext('Database activity') : gettext('Server activity')}
|
||||
>
|
||||
{dbConnected ? gettext('Database activity') : gettext('Server activity')}{' '}
|
||||
</Box>
|
||||
<Box height="100%" display="flex" flexDirection="column">
|
||||
<Box>
|
||||
<Tabs
|
||||
value={tabVal}
|
||||
onChange={tabChanged}
|
||||
>
|
||||
{tabs.map((tabValue) => {
|
||||
return <Tab key={tabValue} label={tabValue} />;
|
||||
})}
|
||||
<RefreshButton/>
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={tabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
CustomHeader={CustomActiveOnlyHeader}
|
||||
columns={activityColumns}
|
||||
data={filteredDashData}
|
||||
schema={schemaDict}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
columns={databaseLocksColumns}
|
||||
data={dashData}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={2} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
columns={databasePreparedColumns}
|
||||
data={dashData}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={3} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
columns={serverConfigColumns}
|
||||
data={dashData}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</TabPanel>
|
||||
{/* System Statistics */}
|
||||
<TabPanel value={mainTabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<Box height="100%" display="flex" flexDirection="column">
|
||||
{ssMsg === 'installed' && did === ldid ?
|
||||
<ErrorBoundary>
|
||||
<Box>
|
||||
<Tabs
|
||||
value={systemStatsTabVal}
|
||||
onChange={systemStatsTabChanged}
|
||||
>
|
||||
{systemStatsTabs.map((tabValue) => {
|
||||
return <Tab key={tabValue} label={tabValue} />;
|
||||
})}
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={systemStatsTabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
<Summary
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.isActive}
|
||||
serverConnected={serverConnected}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={systemStatsTabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<CPU
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.isActive}
|
||||
serverConnected={serverConnected}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={systemStatsTabVal} index={2} classNameRoot={classes.tabPanel}>
|
||||
<Memory
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.isActive}
|
||||
serverConnected={serverConnected}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={systemStatsTabVal} index={3} classNameRoot={classes.tabPanel}>
|
||||
<Storage
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
sid={sid}
|
||||
did={did}
|
||||
pageVisible={props.isActive}
|
||||
serverConnected={serverConnected}
|
||||
systemStatsTabVal={systemStatsTabVal}
|
||||
/>
|
||||
</TabPanel>
|
||||
</ErrorBoundary> :
|
||||
<div className={classes.emptyPanel}>
|
||||
<EmptyPanelMessage text={ssMsg}/>
|
||||
</div>
|
||||
}
|
||||
</Box>
|
||||
</TabPanel>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
@ -1069,48 +1078,14 @@ Dashboard.propTypes = {
|
||||
itemData: PropTypes.object,
|
||||
nodeData: PropTypes.object,
|
||||
treeNodeInfo: PropTypes.object,
|
||||
item: PropTypes.object,
|
||||
pgBrowser: PropTypes.object,
|
||||
nodeItem: PropTypes.object,
|
||||
preferences: PropTypes.object,
|
||||
sid: PropTypes.string,
|
||||
did: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
|
||||
row: PropTypes.object,
|
||||
serverConnected: PropTypes.bool,
|
||||
dbConnected: PropTypes.bool,
|
||||
panelVisible: PropTypes.bool,
|
||||
isActive: PropTypes.bool,
|
||||
};
|
||||
|
||||
export function ChartContainer(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Card className={classes.chartCard} elevation={0}>
|
||||
<CardHeader title={<Box display="flex" justifyContent="space-between">
|
||||
<div id={props.id}>{props.title}</div>
|
||||
<div className={classes.chartLegend}>
|
||||
<div className="d-flex">
|
||||
{props.datasets?.map((datum)=>(
|
||||
<div className="legend-value" key={datum.label}>
|
||||
<span style={{backgroundColor: datum.borderColor}}> </span>
|
||||
<span className="legend-label">{datum.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Box>} />
|
||||
<CardContent className={classes.chartCardContent}>
|
||||
{!props.errorMsg && !props.isTest && props.children}
|
||||
{props.errorMsg && <EmptyPanelMessage text={props.errorMsg}/>}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
ChartContainer.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
datasets: PropTypes.array.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
errorMsg: PropTypes.string,
|
||||
isTest: PropTypes.bool
|
||||
};
|
||||
export default withStandardTabInfo(Dashboard, BROWSER_PANELS.DASHBOARD);
|
||||
|
@ -8,7 +8,7 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React, { useEffect, useRef, useState, useReducer, useMemo } from 'react';
|
||||
import { DATA_POINT_SIZE } from 'sources/chartjs';
|
||||
import {ChartContainer} from './Dashboard';
|
||||
import ChartContainer from './ChartContainer';
|
||||
import url_for from 'sources/url_for';
|
||||
import axios from 'axios';
|
||||
import gettext from 'sources/gettext';
|
||||
|
@ -13,7 +13,7 @@ import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import {getGCD, getEpoch} from 'sources/utils';
|
||||
import {ChartContainer} from '../Dashboard';
|
||||
import ChartContainer from '../ChartContainer';
|
||||
import { Box, Grid } from '@material-ui/core';
|
||||
import { DATA_POINT_SIZE } from 'sources/chartjs';
|
||||
import StreamingChart from '../../../../static/js/components/PgChart/StreamingChart';
|
||||
|
@ -12,7 +12,7 @@ import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import {getGCD, getEpoch} from 'sources/utils';
|
||||
import {ChartContainer} from '../Dashboard';
|
||||
import ChartContainer from '../ChartContainer';
|
||||
import { Box, Grid } from '@material-ui/core';
|
||||
import { DATA_POINT_SIZE } from 'sources/chartjs';
|
||||
import StreamingChart from '../../../../static/js/components/PgChart/StreamingChart';
|
||||
|
@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import url_for from 'sources/url_for';
|
||||
import {getGCD, getEpoch} from 'sources/utils';
|
||||
import {ChartContainer} from '../Dashboard';
|
||||
import ChartContainer from '../ChartContainer';
|
||||
import { Grid } from '@material-ui/core';
|
||||
import { DATA_POINT_SIZE } from 'sources/chartjs';
|
||||
import StreamingChart from '../../../../static/js/components/PgChart/StreamingChart';
|
||||
|
@ -13,7 +13,7 @@ import { makeStyles } from '@material-ui/core/styles';
|
||||
import url_for from 'sources/url_for';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import {getGCD, getEpoch} from 'sources/utils';
|
||||
import {ChartContainer} from '../Dashboard';
|
||||
import ChartContainer from '../ChartContainer';
|
||||
import { Grid } from '@material-ui/core';
|
||||
import { DATA_POINT_SIZE } from 'sources/chartjs';
|
||||
import StreamingChart from '../../../../static/js/components/PgChart/StreamingChart';
|
||||
@ -277,7 +277,7 @@ Summary.propTypes = {
|
||||
enablePoll: PropTypes.bool,
|
||||
};
|
||||
|
||||
export function SummaryWrapper(props) {
|
||||
function SummaryWrapper(props) {
|
||||
const classes = useStyles();
|
||||
const options = useMemo(()=>({
|
||||
showDataPoints: props.showDataPoints,
|
||||
|
@ -10,11 +10,11 @@
|
||||
import React from 'react';
|
||||
import gettext from 'sources/gettext';
|
||||
import _ from 'lodash';
|
||||
import { Link, BrowserRouter } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import PgAdminLogo from './PgAdminLogo';
|
||||
import { Link } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
@ -119,118 +119,116 @@ export default function WelcomeDashboard({ pgBrowser }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<div className={classes.emptyPanel}>
|
||||
<div className={classes.dashboardContainer}>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.cardColumn}>
|
||||
<div className={classes.card}>
|
||||
<div className={classes.cardHeader}>{gettext('Welcome')}</div>
|
||||
<div className={classes.cardBody}>
|
||||
<PgAdminLogo />
|
||||
<h4>
|
||||
{gettext('Feature rich')} | {gettext('Maximises PostgreSQL')}{' '}
|
||||
| {gettext('Open Source')}{' '}
|
||||
</h4>
|
||||
<p>
|
||||
{gettext(
|
||||
'pgAdmin is an Open Source administration and management tool for the PostgreSQL database. It includes a graphical administration interface, an SQL query tool, a procedural code debugger and much more. The tool is designed to answer the needs of developers, DBAs and system administrators alike.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className={classes.emptyPanel}>
|
||||
<div className={classes.dashboardContainer}>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.cardColumn}>
|
||||
<div className={classes.card}>
|
||||
<div className={classes.cardHeader}>{gettext('Welcome')}</div>
|
||||
<div className={classes.cardBody}>
|
||||
<PgAdminLogo />
|
||||
<h4>
|
||||
{gettext('Feature rich')} | {gettext('Maximises PostgreSQL')}{' '}
|
||||
| {gettext('Open Source')}{' '}
|
||||
</h4>
|
||||
<p>
|
||||
{gettext(
|
||||
'pgAdmin is an Open Source administration and management tool for the PostgreSQL database. It includes a graphical administration interface, an SQL query tool, a procedural code debugger and much more. The tool is designed to answer the needs of developers, DBAs and system administrators alike.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.cardColumn}>
|
||||
<div className={classes.card}>
|
||||
<div className={classes.cardHeader}>{gettext('Quick Links')}</div>
|
||||
<div className={classes.cardBody}>
|
||||
<div className={classes.rowContent}>
|
||||
<div className={classes.dashboardLink}>
|
||||
<Link to="#" onClick={() => { AddNewServer(pgBrowser); }} className={classes.link}>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon fa-server"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('Add New Server')}
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classes.dashboardLink}>
|
||||
<Link to="#" onClick={() => pgAdmin.Preferences.show()} className={classes.link}>
|
||||
<span
|
||||
id="mnu_preferences"
|
||||
className="fa fa-4x dashboard-icon fa-cogs"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('Configure pgAdmin')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.cardColumn}>
|
||||
<div className={classes.card}>
|
||||
<div className={classes.cardHeader}>{gettext('Quick Links')}</div>
|
||||
<div className={classes.cardBody}>
|
||||
<div className={classes.rowContent}>
|
||||
<div className={classes.dashboardLink}>
|
||||
<Link onClick={() => { AddNewServer(pgBrowser); }} className={classes.link}>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon fa-server"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('Add New Server')}
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classes.dashboardLink}>
|
||||
<Link onClick={() => pgAdmin.Preferences.show()} className={classes.link}>
|
||||
<span
|
||||
id="mnu_preferences"
|
||||
className="fa fa-4x dashboard-icon fa-cogs"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('Configure pgAdmin')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.cardColumn}>
|
||||
<div className={classes.card}>
|
||||
<div className={classes.cardHeader}>{gettext('Getting Started')}</div>
|
||||
<div className={classes.cardBody}>
|
||||
<div className={classes.rowContent}>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<a
|
||||
href="https://www.postgresql.org/docs"
|
||||
target="postgres_help"
|
||||
className={classes.link}
|
||||
>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon dashboard-pg-doc"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('PostgreSQL Documentation')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<a href="https://www.pgadmin.org" target="pgadmin_website" className={classes.link}>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon fa-globe"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('pgAdmin Website')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<a
|
||||
href="https://planet.postgresql.org"
|
||||
target="planet_website"
|
||||
className={classes.link}
|
||||
>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon fa-book"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('Planet PostgreSQL')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<a
|
||||
href="https://www.postgresql.org/community"
|
||||
target="postgres_website"
|
||||
className={classes.link}
|
||||
>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon fa-users"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('Community Support')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.cardColumn}>
|
||||
<div className={classes.card}>
|
||||
<div className={classes.cardHeader}>{gettext('Getting Started')}</div>
|
||||
<div className={classes.cardBody}>
|
||||
<div className={classes.rowContent}>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<a
|
||||
href="https://www.postgresql.org/docs"
|
||||
target="postgres_help"
|
||||
className={classes.link}
|
||||
>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon dashboard-pg-doc"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('PostgreSQL Documentation')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<a href="https://www.pgadmin.org" target="pgadmin_website" className={classes.link}>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon fa-globe"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('pgAdmin Website')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<a
|
||||
href="https://planet.postgresql.org"
|
||||
target="planet_website"
|
||||
className={classes.link}
|
||||
>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon fa-book"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('Planet PostgreSQL')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<a
|
||||
href="https://www.postgresql.org/community"
|
||||
target="postgres_website"
|
||||
className={classes.link}
|
||||
>
|
||||
<span
|
||||
className="fa fa-4x dashboard-icon fa-users"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<br />
|
||||
{gettext('Community Support')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -238,7 +236,7 @@ export default function WelcomeDashboard({ pgBrowser }) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -11723,7 +11723,7 @@ msgstr ""
|
||||
#: pgadmin/static/js/Dialogs/ConnectServerContent.jsx:136
|
||||
#: pgadmin/static/js/Dialogs/MasterPasswordContent.jsx:142
|
||||
#: pgadmin/static/js/Dialogs/NamedRestoreContent.jsx:88
|
||||
#: pgadmin/static/js/Dialogs/RenamePanelContent.jsx:115
|
||||
#: pgadmin/static/js/Dialogs/RenameTabContent.jsx:115
|
||||
#: pgadmin/static/js/helpers/ModalProvider.jsx:52
|
||||
#: pgadmin/static/js/helpers/ModalProvider.jsx:75
|
||||
#: pgadmin/static/js/helpers/Notifier.jsx:107
|
||||
@ -11772,7 +11772,7 @@ msgstr ""
|
||||
msgid "Enter the name of the restore point to add"
|
||||
msgstr ""
|
||||
|
||||
#: pgadmin/static/js/Dialogs/RenamePanelContent.jsx:85
|
||||
#: pgadmin/static/js/Dialogs/RenameTabContent.jsx:85
|
||||
msgid "Title cannot be empty"
|
||||
msgstr ""
|
||||
|
||||
|
@ -19,13 +19,16 @@ from pgadmin.utils.csrf import pgCSRFProtect
|
||||
from pgadmin.utils.session import cleanup_session_files
|
||||
from pgadmin.misc.themes import get_all_themes
|
||||
from pgadmin.utils.constants import MIMETYPE_APP_JS, UTILITIES_ARRAY
|
||||
from pgadmin.utils.ajax import precondition_required, make_json_response
|
||||
from pgadmin.utils.heartbeat import log_server_heartbeat,\
|
||||
from pgadmin.utils.ajax import precondition_required, make_json_response, \
|
||||
internal_server_error
|
||||
from pgadmin.utils.heartbeat import log_server_heartbeat, \
|
||||
get_server_heartbeat, stop_server_heartbeat
|
||||
import config
|
||||
import subprocess
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import os
|
||||
from urllib.request import urlopen
|
||||
from pgadmin.settings import get_setting, store_setting
|
||||
|
||||
MODULE_NAME = 'misc'
|
||||
|
||||
@ -72,7 +75,7 @@ class MiscModule(PgAdminModule):
|
||||
'value': theme,
|
||||
'preview_src': url_for(
|
||||
'static', filename='js/generated/img/' +
|
||||
theme_data['preview_img']
|
||||
theme_data['preview_img']
|
||||
)
|
||||
})
|
||||
|
||||
@ -97,7 +100,8 @@ class MiscModule(PgAdminModule):
|
||||
"""
|
||||
return ['misc.ping', 'misc.index', 'misc.cleanup',
|
||||
'misc.validate_binary_path', 'misc.log_heartbeat',
|
||||
'misc.stop_heartbeat', 'misc.get_heartbeat']
|
||||
'misc.stop_heartbeat', 'misc.get_heartbeat',
|
||||
'misc.upgrade_check']
|
||||
|
||||
def register(self, app, options):
|
||||
"""
|
||||
@ -200,24 +204,6 @@ def get_heartbeat(sid):
|
||||
status=200)
|
||||
|
||||
|
||||
@blueprint.route("/explain/explain.js")
|
||||
def explain_js():
|
||||
"""
|
||||
explain_js()
|
||||
|
||||
Returns:
|
||||
javascript for the explain module
|
||||
"""
|
||||
return Response(
|
||||
response=render_template(
|
||||
"explain/js/explain.js",
|
||||
_=gettext
|
||||
),
|
||||
status=200,
|
||||
mimetype=MIMETYPE_APP_JS
|
||||
)
|
||||
|
||||
|
||||
##########################################################################
|
||||
# A special URL used to shut down the server
|
||||
##########################################################################
|
||||
@ -266,3 +252,56 @@ def validate_binary_path():
|
||||
return precondition_required(gettext('Invalid binary path.'))
|
||||
|
||||
return make_json_response(data=gettext(version_str), status=200)
|
||||
|
||||
|
||||
@blueprint.route("/upgrade_check", endpoint="upgrade_check", methods=['GET'])
|
||||
@login_required
|
||||
def upgrade_check():
|
||||
# Get the current version info from the website, and flash a message if
|
||||
# the user is out of date, and the check is enabled.
|
||||
ret = {
|
||||
"outdated": False,
|
||||
}
|
||||
if config.UPGRADE_CHECK_ENABLED:
|
||||
last_check = get_setting('LastUpdateCheck', default='0')
|
||||
today = time.strftime('%Y%m%d')
|
||||
if int(last_check) < int(today):
|
||||
data = None
|
||||
url = '%s?version=%s' % (
|
||||
config.UPGRADE_CHECK_URL, config.APP_VERSION)
|
||||
current_app.logger.debug('Checking version data at: %s' % url)
|
||||
try:
|
||||
# Do not wait for more than 5 seconds.
|
||||
# It stuck on rendering the browser.html, while working in the
|
||||
# broken network.
|
||||
if os.path.exists(config.CA_FILE):
|
||||
response = urlopen(url, data, 5, cafile=config.CA_FILE)
|
||||
else:
|
||||
response = urlopen(url, data, 5)
|
||||
current_app.logger.debug(
|
||||
'Version check HTTP response code: %d' % response.getcode()
|
||||
)
|
||||
|
||||
if response.getcode() == 200:
|
||||
data = json.loads(response.read().decode('utf-8'))
|
||||
current_app.logger.debug('Response data: %s' % data)
|
||||
except Exception:
|
||||
current_app.logger.exception(
|
||||
'Exception when checking for update')
|
||||
return internal_server_error('Failed to check for update')
|
||||
|
||||
if data is not None and \
|
||||
data[config.UPGRADE_CHECK_KEY]['version_int'] > \
|
||||
config.APP_VERSION_INT:
|
||||
ret = {
|
||||
"outdated": True,
|
||||
"current_version": config.APP_VERSION,
|
||||
"upgrade_version": data[config.UPGRADE_CHECK_KEY][
|
||||
'version'],
|
||||
"product_name": config.APP_NAME,
|
||||
"download_url": data[config.UPGRADE_CHECK_KEY][
|
||||
'download_url']
|
||||
}
|
||||
|
||||
store_setting('LastUpdateCheck', today)
|
||||
return make_json_response(data=ret)
|
||||
|
21
web/pgadmin/misc/bgprocess/static/js/BgProcessConstants.js
Normal file
21
web/pgadmin/misc/bgprocess/static/js/BgProcessConstants.js
Normal file
@ -0,0 +1,21 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
export const BgProcessManagerEvents = {
|
||||
LIST_UPDATED: 'LIST_UPDATED',
|
||||
};
|
||||
|
||||
export const BgProcessManagerProcessState = {
|
||||
PROCESS_NOT_STARTED: 0,
|
||||
PROCESS_STARTED: 1,
|
||||
PROCESS_FINISHED: 2,
|
||||
PROCESS_TERMINATED: 3,
|
||||
/* Supported by front end only */
|
||||
PROCESS_TERMINATING: 10,
|
||||
PROCESS_FAILED: 11,
|
||||
};
|
@ -6,32 +6,18 @@
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import getApiInstance, { parseApiError } from '../../../../static/js/api_instance';
|
||||
import url_for from 'sources/url_for';
|
||||
import Notifier from '../../../../static/js/helpers/Notifier';
|
||||
import EventBus from '../../../../static/js/helpers/EventBus';
|
||||
import * as BgProcessNotify from './BgProcessNotify';
|
||||
import showDetails from './showDetails';
|
||||
import gettext from 'sources/gettext';
|
||||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import * as BgProcessNotify from './BgProcessNotify';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import { processesPanelData } from '../../../../static/js/BrowserComponent';
|
||||
import { BgProcessManagerEvents, BgProcessManagerProcessState } from './BgProcessConstants';
|
||||
|
||||
const WORKER_INTERVAL = 1000;
|
||||
|
||||
export const BgProcessManagerEvents = {
|
||||
LIST_UPDATED: 'LIST_UPDATED',
|
||||
};
|
||||
|
||||
export const BgProcessManagerProcessState = {
|
||||
PROCESS_NOT_STARTED: 0,
|
||||
PROCESS_STARTED: 1,
|
||||
PROCESS_FINISHED: 2,
|
||||
PROCESS_TERMINATED: 3,
|
||||
/* Supported by front end only */
|
||||
PROCESS_TERMINATING: 10,
|
||||
PROCESS_FAILED: 11,
|
||||
};
|
||||
|
||||
|
||||
export default class BgProcessManager {
|
||||
static instance;
|
||||
|
||||
@ -148,7 +134,7 @@ export default class BgProcessManager {
|
||||
this._eventManager.fireEvent(BgProcessManagerEvents.LIST_UPDATED);
|
||||
})
|
||||
.catch((err)=>{
|
||||
Notifier.error(parseApiError(err));
|
||||
pgAdmin.Browser.notifier.error(parseApiError(err));
|
||||
});
|
||||
}
|
||||
|
||||
@ -169,16 +155,12 @@ export default class BgProcessManager {
|
||||
/* Object not available */
|
||||
removeJob(jobId);
|
||||
} else {
|
||||
Notifier.error(parseApiError(err));
|
||||
pgAdmin.Browser.notifier.error(parseApiError(err));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
viewJobDetails(jobId) {
|
||||
showDetails(this.procList.find((p)=>p.id==jobId));
|
||||
}
|
||||
|
||||
updateCloudDetails(jobId) {
|
||||
this.api.put(url_for('bgprocess.update_cloud_details', {
|
||||
pid: jobId,
|
||||
@ -186,7 +168,7 @@ export default class BgProcessManager {
|
||||
.then((res)=>{
|
||||
let _server = res.data?.data?.node;
|
||||
if(!_server) {
|
||||
Notifier.error(gettext('Cloud server deployment is pending'));
|
||||
pgAdmin.Browser.notifier.error(gettext('Cloud server deployment is pending'));
|
||||
return;
|
||||
}
|
||||
let _server_path = '/browser/server_group_' + _server.gid + '/' + _server.id,
|
||||
@ -209,7 +191,7 @@ export default class BgProcessManager {
|
||||
})
|
||||
.catch((err)=>{
|
||||
if(err.response?.status != 410) {
|
||||
Notifier.error(gettext('Failed Cloud Deployment.'));
|
||||
pgAdmin.Browser.notifier.error(gettext('Failed Cloud Deployment.'));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -223,14 +205,12 @@ export default class BgProcessManager {
|
||||
}
|
||||
|
||||
openProcessesPanel() {
|
||||
let processPanel = this.pgBrowser.docker.findPanels('processes');
|
||||
if(processPanel.length > 0) {
|
||||
processPanel = processPanel[0];
|
||||
let processPanel = this.pgBrowser.docker.find(BROWSER_PANELS.PROCESSES);
|
||||
if(!processPanel) {
|
||||
pgAdmin.Browser.docker.openTab(processesPanelData, BROWSER_PANELS.MAIN, 'middle', true);
|
||||
} else {
|
||||
let propertiesPanel = this.pgBrowser.docker.findPanels('properties');
|
||||
processPanel = this.pgBrowser.docker.addPanel('processes', window.wcDocker.DOCK.STACKED, propertiesPanel[0]);
|
||||
this.pgBrowser.docker.focus(BROWSER_PANELS.PROCESSES);
|
||||
}
|
||||
processPanel.focus();
|
||||
}
|
||||
|
||||
registerListener(event, callback) {
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Box, makeStyles } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import Notifier from '../../../../static/js/helpers/Notifier';
|
||||
import CloseIcon from '@material-ui/icons/CloseRounded';
|
||||
import { DefaultButton, PgIconButton } from '../../../../static/js/components/Buttons';
|
||||
import clsx from 'clsx';
|
||||
import DescriptionOutlinedIcon from '@material-ui/icons/DescriptionOutlined';
|
||||
import { BgProcessManagerProcessState } from './BgProcessManager';
|
||||
import { BgProcessManagerProcessState } from './BgProcessConstants';
|
||||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
@ -77,7 +77,7 @@ ProcessNotifyMessage.propTypes = {
|
||||
|
||||
|
||||
export function processStarted(desc, onViewProcess) {
|
||||
Notifier.notify(
|
||||
pgAdmin.Browser.notifier.notify(
|
||||
<ProcessNotifyMessage title={gettext('Process started')} desc={desc} onViewProcess={onViewProcess} dataTestSuffix="start"/>,
|
||||
null
|
||||
);
|
||||
@ -94,7 +94,7 @@ export function processCompleted(desc, process_state, onViewProcess) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
Notifier.notify(
|
||||
pgAdmin.Browser.notifier.notify(
|
||||
<ProcessNotifyMessage title={title} desc={desc} onViewProcess={onViewProcess} success={success} dataTestSuffix="end"/>,
|
||||
null
|
||||
);
|
||||
|
@ -13,7 +13,7 @@ import url_for from 'sources/url_for';
|
||||
import { Box, makeStyles } from '@material-ui/core';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MESSAGE_TYPE, NotifierMessage } from '../../../../static/js/components/FormComponents';
|
||||
import { BgProcessManagerProcessState } from './BgProcessManager';
|
||||
import { BgProcessManagerProcessState } from './BgProcessConstants';
|
||||
import { DefaultButton, PgIconButton } from '../../../../static/js/components/Buttons';
|
||||
import HighlightOffRoundedIcon from '@material-ui/icons/HighlightOffRounded';
|
||||
import AccessTimeRoundedIcon from '@material-ui/icons/AccessTimeRounded';
|
||||
|
@ -7,13 +7,12 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import { BgProcessManagerEvents, BgProcessManagerProcessState } from './BgProcessManager';
|
||||
import { BgProcessManagerEvents, BgProcessManagerProcessState } from './BgProcessConstants';
|
||||
import { PgIconButton } from '../../../../static/js/components/Buttons';
|
||||
import CancelIcon from '@material-ui/icons/Cancel';
|
||||
import DescriptionOutlinedIcon from '@material-ui/icons/DescriptionOutlined';
|
||||
@ -21,7 +20,10 @@ import DeleteIcon from '@material-ui/icons/Delete';
|
||||
import HelpIcon from '@material-ui/icons/HelpRounded';
|
||||
import url_for from 'sources/url_for';
|
||||
import { Box } from '@material-ui/core';
|
||||
import Notifier from '../../../../static/js/helpers/Notifier';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import ErrorBoundary from '../../../../static/js/helpers/ErrorBoundary';
|
||||
import ProcessDetails from './ProcessDetails';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
@ -93,13 +95,33 @@ const ProcessStateTextAndColor = {
|
||||
[BgProcessManagerProcessState.PROCESS_TERMINATING]: [gettext('Terminating...'), 'bgTerm'],
|
||||
[BgProcessManagerProcessState.PROCESS_FAILED]: [gettext('Failed'), 'bgFailed'],
|
||||
};
|
||||
|
||||
export default function Processes() {
|
||||
const classes = useStyles();
|
||||
const pgAdmin = usePgAdmin();
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
const [selectedRows, setSelectedRows] = React.useState([]);
|
||||
|
||||
let columns = useMemo(()=>[
|
||||
const onViewDetailsClick = useCallback((p)=>{
|
||||
const panelTitle = gettext('Process Watcher - %s', p.type_desc);
|
||||
const panelId = BROWSER_PANELS.PROCESS_DETAILS+''+p.id;
|
||||
pgAdmin.Browser.docker.openDialog({
|
||||
id: panelId,
|
||||
title: panelTitle,
|
||||
content: (
|
||||
<ErrorBoundary>
|
||||
<ProcessDetails
|
||||
data={p}
|
||||
closeModal={()=>{
|
||||
pgAdmin.Browser.docker.close(panelId);
|
||||
}}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}, pgAdmin.Browser.stdW.md);
|
||||
}, []);
|
||||
|
||||
|
||||
const columns = useMemo(()=>[
|
||||
{
|
||||
accessor: 'stop_process',
|
||||
Header: () => null,
|
||||
@ -149,7 +171,7 @@ export default function Processes() {
|
||||
noBorder
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
pgAdmin.Browser.BgProcessManager.viewJobDetails(row.original.id);
|
||||
onViewDetailsClick(row.original);
|
||||
}}
|
||||
aria-label="View details"
|
||||
title={gettext('View details')}
|
||||
@ -266,7 +288,7 @@ export default function Processes() {
|
||||
aria-label="Acknowledge and Remove"
|
||||
title={gettext('Acknowledge and Remove')}
|
||||
onClick={() => {
|
||||
Notifier.confirm(gettext('Remove Processes'), gettext('Are you sure you want to remove the selected processes?'), ()=>{
|
||||
pgAdmin.Browser.notifier.confirm(gettext('Remove Processes'), gettext('Are you sure you want to remove the selected processes?'), ()=>{
|
||||
pgAdmin.Browser.BgProcessManager.acknowledge(selectedRows.map((p)=>p.original.id));
|
||||
});
|
||||
}}
|
||||
|
@ -1,31 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import Theme from '../../../../static/js/Theme';
|
||||
import ProcessDetails from './ProcessDetails';
|
||||
import gettext from 'sources/gettext';
|
||||
|
||||
export default function showDetails(p) {
|
||||
let pgBrowser = pgAdmin.Browser;
|
||||
|
||||
// Register dialog panel
|
||||
pgBrowser.Node.registerUtilityPanel();
|
||||
let panel = pgBrowser.Node.addUtilityPanel(pgBrowser.stdW.md),
|
||||
j = panel.$container.find('.obj_properties').first();
|
||||
panel.title(gettext('Process Watcher - %s', p.type_desc));
|
||||
panel.focus();
|
||||
|
||||
panel.on(window.wcDocker.EVENT.CLOSED, ()=>{
|
||||
ReactDOM.unmountComponentAtNode(j[0]);
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<ProcessDetails
|
||||
data={p}
|
||||
closeModal={()=>{
|
||||
panel.close();
|
||||
}}
|
||||
/>
|
||||
</Theme>, j[0]);
|
||||
}
|
@ -16,7 +16,6 @@ import Wizard from '../../../../static/js/helpers/wizard/Wizard';
|
||||
import WizardStep from '../../../../static/js/helpers/wizard/WizardStep';
|
||||
import {FormFooterMessage, MESSAGE_TYPE } from '../../../../static/js/components/FormComponents';
|
||||
import getApiInstance from '../../../../static/js/api_instance';
|
||||
import Notifier from '../../../../static/js/helpers/Notifier';
|
||||
import PropTypes from 'prop-types';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import {ToggleButtons, FinalSummary} from './cloud_components';
|
||||
@ -29,6 +28,7 @@ import {AzureCredentials, AzureInstanceDetails, AzureDatabaseDetails, checkClust
|
||||
import { GoogleCredentials, GoogleInstanceDetails, GoogleDatabaseDetails, validateGoogleStep2, validateGoogleStep3 } from './google';
|
||||
import EventBus from '../../../../static/js/helpers/EventBus';
|
||||
import { CLOUD_PROVIDERS, CLOUD_PROVIDERS_LABELS } from './cloud_constants';
|
||||
import { LAYOUT_EVENTS } from '../../../../static/js/helpers/Layout';
|
||||
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
@ -64,7 +64,7 @@ const useStyles = makeStyles(() =>
|
||||
|
||||
export const CloudWizardEventsContext = React.createContext();
|
||||
|
||||
export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel}) {
|
||||
export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanelId}) {
|
||||
const classes = useStyles();
|
||||
const eventBus = React.useRef(new EventBus());
|
||||
|
||||
@ -98,16 +98,27 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
const [verificationURI, setVerificationURI] = React.useState('');
|
||||
const [verificationCode, setVerificationCode] = React.useState('');
|
||||
|
||||
const authInterval = React.useRef();
|
||||
|
||||
React.useEffect(()=>{
|
||||
eventBus.current.registerListener('SET_ERROR_MESSAGE_FOR_CLOUD_WIZARD', (msg) => {
|
||||
setErrMsg(msg);
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useEffect(()=>{
|
||||
eventBus.current.registerListener('SET_CRED_VERIFICATION_INITIATED', (initiated) => {
|
||||
setVerificationIntiated(initiated);
|
||||
});
|
||||
|
||||
const onWizardClosing = (panelId)=>{
|
||||
if(panelId == cloudPanelId) {
|
||||
clearInterval(authInterval.current);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
pgAdmin.Browser.docker.eventBus.registerListener(LAYOUT_EVENTS.CLOSING, onWizardClosing);
|
||||
return ()=>{
|
||||
pgAdmin.Browser.docker.eventBus.deregisterListener(LAYOUT_EVENTS.CLOSING, onWizardClosing);
|
||||
};
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -119,7 +130,7 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
Notifier.error(gettext(`Error while getting the host ip: ${error.response.data.errormsg}`));
|
||||
pgAdmin.Browser.notifier.error(gettext(`Error while getting the host ip: ${error.response.data.errormsg}`));
|
||||
});
|
||||
}, [cloudProvider]);
|
||||
|
||||
@ -173,7 +184,7 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
onClose();
|
||||
})
|
||||
.catch((error) => {
|
||||
Notifier.error(gettext(`Error while saving cloud wizard data: ${error.response.data.errormsg}`));
|
||||
pgAdmin.Browser.notifier.error(gettext(`Error while saving cloud wizard data: ${error.response.data.errormsg}`));
|
||||
});
|
||||
};
|
||||
|
||||
@ -366,37 +377,33 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
let child = window.open(verificationURI, 'edb_biganimal_authentication');
|
||||
let _url = url_for('biganimal.verification_ack') ;
|
||||
let countdown = 60;
|
||||
const myInterval = setInterval(() => {
|
||||
authInterval.current = setInterval(() => {
|
||||
axiosApi.get(_url)
|
||||
.then((res) => {
|
||||
if (res.data && res.data.success == 1 ) {
|
||||
setErrMsg([MESSAGE_TYPE.SUCCESS, gettext('Authentication completed successfully. Click the Next button to proceed.')]);
|
||||
setVerificationIntiated(true);
|
||||
clearInterval(myInterval);
|
||||
clearInterval(authInterval.current);
|
||||
} else if (res.data && res.data.success == 0 && res.data.errormsg == 'access_denied') {
|
||||
setErrMsg([MESSAGE_TYPE.INFO, gettext('Verification failed. Access Denied...')]);
|
||||
setVerificationIntiated(false);
|
||||
clearInterval(myInterval);
|
||||
clearInterval(authInterval.current);
|
||||
} else if (res.data && res.data.success == 0 && res.data.errormsg == 'forbidden') {
|
||||
setErrMsg([MESSAGE_TYPE.INFO, gettext('Authentication completed successfully but you do not have permission to create the cluster.')]);
|
||||
setVerificationIntiated(false);
|
||||
clearInterval(myInterval);
|
||||
clearInterval(authInterval.current);
|
||||
} else if (child.closed && !verificationIntiated && countdown <= 0) {
|
||||
setVerificationIntiated(false);
|
||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext('Authentication is aborted.')]);
|
||||
clearInterval(myInterval);
|
||||
clearInterval(authInterval.current);
|
||||
}
|
||||
authInterval.current = null;
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext(`Error while verifying EDB BigAnimal: ${error.response.data.errormsg}`)]);
|
||||
});
|
||||
countdown = countdown - 1;
|
||||
}, 1000);
|
||||
|
||||
cloudPanel.on(window.wcDocker.EVENT.CLOSED, function() {
|
||||
clearInterval(myInterval);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -575,5 +582,6 @@ CloudWizard.propTypes = {
|
||||
nodeInfo: PropTypes.object,
|
||||
nodeData: PropTypes.object,
|
||||
onClose: PropTypes.func,
|
||||
cloudPanel: PropTypes.object
|
||||
cloudPanel: PropTypes.object,
|
||||
cloudPanelId: PropTypes.string,
|
||||
};
|
||||
|
@ -7,12 +7,10 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Theme from 'sources/Theme';
|
||||
import CloudWizard from './CloudWizard';
|
||||
import getApiInstance from '../../../../static/js/api_instance';
|
||||
import Notifier from '../../../../static/js/helpers/Notifier';
|
||||
|
||||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
// Cloud Wizard
|
||||
define('pgadmin.misc.cloud', [
|
||||
@ -74,30 +72,26 @@ define('pgadmin.misc.cloud', [
|
||||
d = this.d = i ? t.itemData(i) : undefined,
|
||||
info = this.info = pgBrowser.tree.getTreeNodeHierarchy(i);
|
||||
|
||||
// Register dialog panel
|
||||
pgBrowser.Node.registerUtilityPanel();
|
||||
let panel = pgBrowser.Node.addUtilityPanel(930, 650),
|
||||
j = panel.$container.find('.obj_properties').first();
|
||||
panel.title(gettext('Deploy Cloud Instance'));
|
||||
|
||||
panel.on(window.wcDocker.EVENT.CLOSED, function() {
|
||||
const axiosApi = getApiInstance();
|
||||
let _url = url_for('cloud.clear_cloud_session');
|
||||
axiosApi.post(_url)
|
||||
.then(() => {/*This is intentional (SonarQube)*/})
|
||||
.catch((error) => {
|
||||
Notifier.error(gettext(`Error while clearing cloud wizard data: ${error.response.data.errormsg}`));
|
||||
});
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<CloudWizard nodeInfo={info} nodeData={d} cloudPanel={panel}
|
||||
const panelTitle = gettext('Deploy Cloud Instance');
|
||||
const panelId = BROWSER_PANELS.CLOUD_WIZARD;
|
||||
pgAdmin.Browser.docker.openDialog({
|
||||
id: panelId,
|
||||
title: panelTitle,
|
||||
manualClose: true,
|
||||
content: (
|
||||
<CloudWizard nodeInfo={info} nodeData={d} cloudPanelId={panelId}
|
||||
onClose={() => {
|
||||
ReactDOM.unmountComponentAtNode(j[0]);
|
||||
panel.close();
|
||||
const axiosApi = getApiInstance();
|
||||
let _url = url_for('cloud.clear_cloud_session');
|
||||
axiosApi.post(_url)
|
||||
.then(() => {/*This is intentional (SonarQube)*/})
|
||||
.catch((error) => {
|
||||
pgAdmin.Browser.notifier.error(gettext(`Error while clearing cloud wizard data: ${error.response.data.errormsg}`));
|
||||
});
|
||||
pgAdmin.Browser.docker.close(panelId, true);
|
||||
}}/>
|
||||
</Theme>, j[0]);
|
||||
)
|
||||
}, pgAdmin.Browser.stdW.lg, pgAdmin.Browser.stdH.lg);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -12,13 +12,15 @@ import React, { useEffect } from 'react';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import Notify from '../../../../static/js/helpers/Notifier';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { getURL } from '../../../static/utils/utils';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import EmptyPanelMessage from '../../../../static/js/components/EmptyPanelMessage';
|
||||
import { parseApiError } from '../../../../static/js/api_instance';
|
||||
import withStandardTabInfo from '../../../../static/js/helpers/withStandardTabInfo';
|
||||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
@ -72,11 +74,13 @@ function parseData(data, node) {
|
||||
return data;
|
||||
}
|
||||
|
||||
export default function Dependencies({ nodeData, item, node, ...props }) {
|
||||
function Dependencies({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale, setIsStale }) {
|
||||
const classes = useStyles();
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
const [msg, setMsg] = React.useState('');
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
let columns = [
|
||||
{
|
||||
Header: 'Type',
|
||||
@ -103,14 +107,18 @@ export default function Dependencies({ nodeData, item, node, ...props }) {
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if(!isStale || !isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = gettext('Please select an object in the tree view.');
|
||||
if (node) {
|
||||
let url = getURL(
|
||||
nodeData,
|
||||
true,
|
||||
props.treeNodeInfo,
|
||||
treeNodeInfo,
|
||||
node,
|
||||
item,
|
||||
nodeItem,
|
||||
'dependency'
|
||||
);
|
||||
message = gettext(
|
||||
@ -134,7 +142,7 @@ export default function Dependencies({ nodeData, item, node, ...props }) {
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Failed to retrieve data from the server.'),
|
||||
parseApiError(e)
|
||||
);
|
||||
@ -147,10 +155,8 @@ export default function Dependencies({ nodeData, item, node, ...props }) {
|
||||
setMsg(message);
|
||||
setLoaderText('');
|
||||
}
|
||||
return () => {
|
||||
setTableData([]);
|
||||
};
|
||||
}, [nodeData]);
|
||||
setIsStale(false);
|
||||
}, [isActive, isStale]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -178,5 +184,10 @@ Dependencies.propTypes = {
|
||||
nodeData: PropTypes.object,
|
||||
treeNodeInfo: PropTypes.object,
|
||||
node: PropTypes.func,
|
||||
item: PropTypes.object,
|
||||
nodeItem: PropTypes.object,
|
||||
isActive: PropTypes.bool,
|
||||
isStale: PropTypes.bool,
|
||||
setIsStale: PropTypes.func,
|
||||
};
|
||||
|
||||
export default withStandardTabInfo(Dependencies, BROWSER_PANELS.DEPENDENCIES);
|
||||
|
@ -12,13 +12,15 @@ import React, { useEffect } from 'react';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import Notify from '../../../../static/js/helpers/Notifier';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { getURL } from '../../../static/utils/utils';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import EmptyPanelMessage from '../../../../static/js/components/EmptyPanelMessage';
|
||||
import { parseApiError } from '../../../../static/js/api_instance';
|
||||
import withStandardTabInfo from '../../../../static/js/helpers/withStandardTabInfo';
|
||||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
@ -72,11 +74,12 @@ function parseData(data, node) {
|
||||
return data;
|
||||
}
|
||||
|
||||
export default function Dependents({ nodeData, item, node, ...props }) {
|
||||
function Dependents({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale, setIsStale }) {
|
||||
const classes = useStyles();
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
const [msg, setMsg] = React.useState('');
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
let columns = [
|
||||
{
|
||||
@ -104,14 +107,18 @@ export default function Dependents({ nodeData, item, node, ...props }) {
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if(!isStale || !isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = gettext('Please select an object in the tree view.');
|
||||
if (node) {
|
||||
let url = getURL(
|
||||
nodeData,
|
||||
true,
|
||||
props.treeNodeInfo,
|
||||
treeNodeInfo,
|
||||
node,
|
||||
item,
|
||||
nodeItem,
|
||||
'dependent'
|
||||
);
|
||||
message = gettext(
|
||||
@ -134,7 +141,7 @@ export default function Dependents({ nodeData, item, node, ...props }) {
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Failed to retrieve data from the server.'),
|
||||
parseApiError(e)
|
||||
);
|
||||
@ -148,10 +155,8 @@ export default function Dependents({ nodeData, item, node, ...props }) {
|
||||
setMsg(message);
|
||||
}
|
||||
|
||||
return () => {
|
||||
setTableData([]);
|
||||
};
|
||||
}, [nodeData, item]);
|
||||
setIsStale(false);
|
||||
}, [isActive, isStale]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -180,5 +185,10 @@ Dependents.propTypes = {
|
||||
nodeData: PropTypes.object,
|
||||
treeNodeInfo: PropTypes.object,
|
||||
node: PropTypes.func,
|
||||
item: PropTypes.object,
|
||||
nodeItem: PropTypes.object,
|
||||
isActive: PropTypes.bool,
|
||||
isStale: PropTypes.bool,
|
||||
setIsStale: PropTypes.func,
|
||||
};
|
||||
|
||||
export default withStandardTabInfo(Dependents, BROWSER_PANELS.DEPENDENTS);
|
||||
|
@ -8,10 +8,10 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import gettext from 'sources/gettext';
|
||||
import Notifier from '../../../../static/js/helpers/Notifier';
|
||||
import React from 'react';
|
||||
import FileManager from './components/FileManager';
|
||||
import { getBrowser } from '../../../../static/js/utils';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
export default class FileManagerModule {
|
||||
static instance;
|
||||
@ -56,7 +56,7 @@ export default class FileManagerModule {
|
||||
}
|
||||
|
||||
showInternal(params, onOK, onCancel, modalObj) {
|
||||
const modal = modalObj || Notifier;
|
||||
const modal = modalObj || pgAdmin.Browser.notifier;
|
||||
let title = params.dialog_title;
|
||||
if(!title) {
|
||||
if(params.dialog_type == 'create_file') {
|
||||
|
@ -130,7 +130,7 @@ export default function GridView({items, operation, onItemSelect, onItemEnter})
|
||||
}
|
||||
|
||||
return (
|
||||
<Box flexGrow={1} overflow="hidden auto">
|
||||
<Box flexGrow={1} overflow="hidden auto" id="grid">
|
||||
<ul ref={gridRef} className={classes.grid}>
|
||||
{items.map((item, i)=>(
|
||||
<ItemView key={item.Filename} idx={i} row={item} selected={selectedIdx==i} onItemSelect={setSelectedIdx}
|
||||
|
@ -153,13 +153,13 @@ export default function ListView({items, operation, ...props}) {
|
||||
}, [operation]);
|
||||
|
||||
useEffect(()=>{
|
||||
gridRef.current.selectCell({idx: 0, rowIdx: 0});
|
||||
gridRef.current?.selectCell({idx: 0, rowIdx: 0});
|
||||
}, [gridRef.current?.element]);
|
||||
|
||||
return (
|
||||
<PgReactDataGrid
|
||||
gridRef={gridRef}
|
||||
id="files"
|
||||
id="list"
|
||||
className={classes.grid}
|
||||
hasSelectColumn={false}
|
||||
columns={columns}
|
||||
|
@ -14,7 +14,6 @@ import FileManagerModule from './FileManagerModule';
|
||||
/* Do not add let, var, const to this variable */
|
||||
__webpack_public_path__ = window.resourceBasePath;
|
||||
/* eslint-enable */
|
||||
|
||||
if(!pgAdmin.Tools) {
|
||||
pgAdmin.Tools = {};
|
||||
}
|
||||
|
@ -7,14 +7,11 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React from 'react';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { Box, Switch } from '@material-ui/core';
|
||||
import { generateCollectionURL } from '../../browser/static/js/node_ajax';
|
||||
import Notify from '../../static/js/helpers/Notifier';
|
||||
import gettext from 'sources/gettext';
|
||||
import 'wcdocker';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import Theme from 'sources/Theme';
|
||||
import PropTypes from 'prop-types';
|
||||
@ -25,6 +22,7 @@ import DeleteForeverIcon from '@material-ui/icons/DeleteForever';
|
||||
import EmptyPanelMessage from '../../static/js/components/EmptyPanelMessage';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import { evalFunc } from '../../static/js/utils';
|
||||
import { usePgAdmin } from '../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
@ -77,14 +75,17 @@ const useStyles = makeStyles((theme) => ({
|
||||
}
|
||||
}));
|
||||
|
||||
export function CollectionNodeView({
|
||||
export default function CollectionNodeProperties({
|
||||
node,
|
||||
treeNodeInfo,
|
||||
itemNodeData,
|
||||
item,
|
||||
pgBrowser
|
||||
nodeData,
|
||||
nodeItem,
|
||||
isActive,
|
||||
isStale,
|
||||
setIsStale
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
const [data, setData] = React.useState([]);
|
||||
const [infoMsg, setInfoMsg] = React.useState('Please select an object in the tree view.');
|
||||
@ -96,7 +97,7 @@ export function CollectionNodeView({
|
||||
//Reload the collection node on refresh or change in children count
|
||||
React.useEffect(() => {
|
||||
setReload(!reload);
|
||||
}, [item?._children]);
|
||||
}, [nodeItem?._children]);
|
||||
|
||||
const [pgTableColumns, setPgTableColumns] = React.useState([
|
||||
{
|
||||
@ -122,9 +123,9 @@ export function CollectionNodeView({
|
||||
const onDrop = (type) => {
|
||||
let selRowModels = selectedObject,
|
||||
selRows = [],
|
||||
selItem = pgBrowser.tree.selected(),
|
||||
selectedItemData = selItem ? pgBrowser.tree.itemData(selItem) : null,
|
||||
selNode = selectedItemData && pgBrowser.Nodes[selectedItemData._type],
|
||||
selItem = pgAdmin.Browser.tree.selected(),
|
||||
selectedItemData = selItem ? pgAdmin.Browser.tree.itemData(selItem) : null,
|
||||
selNode = selectedItemData && pgAdmin.Browser.Nodes[selectedItemData._type],
|
||||
url = undefined,
|
||||
msg = undefined,
|
||||
title = undefined;
|
||||
@ -140,7 +141,7 @@ export function CollectionNodeView({
|
||||
}
|
||||
|
||||
if (selRows.length === 0) {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Delete Multiple'),
|
||||
gettext('Please select at least one object to delete.')
|
||||
);
|
||||
@ -178,14 +179,14 @@ export function CollectionNodeView({
|
||||
.then(function (res) {
|
||||
setLoaderText('');
|
||||
if (res.success == 0) {
|
||||
Notify.alert(res.errormsg, res.info);
|
||||
pgAdmin.Browser.notifier.alert(res.errormsg, res.info);
|
||||
}
|
||||
pgAdmin.Browser.tree.refresh(selItem);
|
||||
setReload(!reload);
|
||||
})
|
||||
.catch(function (error) {
|
||||
setLoaderText('');
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Error deleting %s', selectedItemData._label.toLowerCase()),
|
||||
_.isUndefined(error.response) ? error.message : error.response.data.errormsg
|
||||
);
|
||||
@ -193,7 +194,7 @@ export function CollectionNodeView({
|
||||
};
|
||||
|
||||
if (confirm) {
|
||||
Notify.confirm(title, msg, dropNodeProperties, null);
|
||||
pgAdmin.Browser.notifier.confirm(title, msg, dropNodeProperties, null);
|
||||
} else {
|
||||
dropNodeProperties();
|
||||
}
|
||||
@ -203,9 +204,9 @@ export function CollectionNodeView({
|
||||
if (node){
|
||||
|
||||
let nodeObj =
|
||||
pgAdmin.Browser.Nodes[itemNodeData?._type.replace('coll-', '')];
|
||||
pgAdmin.Browser.Nodes[nodeData?._type.replace('coll-', '')];
|
||||
|
||||
let url = generateCollectionURL.call(nodeObj, item, 'properties');
|
||||
let url = generateCollectionURL.call(nodeObj, nodeItem, 'properties');
|
||||
|
||||
const api = getApiInstance();
|
||||
|
||||
@ -213,8 +214,8 @@ export function CollectionNodeView({
|
||||
let column = {};
|
||||
setLoaderText(gettext('Loading...'));
|
||||
|
||||
if (itemNodeData._type.indexOf('coll-') > -1 && !_.isUndefined(nodeObj.getSchema)) {
|
||||
schemaRef.current = nodeObj.getSchema?.call(nodeObj, treeNodeInfo, itemNodeData);
|
||||
if (nodeData._type.indexOf('coll-') > -1 && !_.isUndefined(nodeObj.getSchema)) {
|
||||
schemaRef.current = nodeObj.getSchema?.call(nodeObj, treeNodeInfo, nodeData);
|
||||
schemaRef.current?.fields.forEach((field) => {
|
||||
if (node.columns.indexOf(field.id) > -1) {
|
||||
if (field.label.indexOf('?') > -1) {
|
||||
@ -257,6 +258,10 @@ export function CollectionNodeView({
|
||||
});
|
||||
}
|
||||
|
||||
if(!isStale || !isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
api({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
@ -271,18 +276,19 @@ export function CollectionNodeView({
|
||||
setLoaderText('');
|
||||
})
|
||||
.catch((err) => {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Failed to retrieve data from the server.'),
|
||||
gettext(err.message)
|
||||
);
|
||||
});
|
||||
setIsStale(false);
|
||||
}
|
||||
}, [itemNodeData, node, item, reload]);
|
||||
}, [nodeData, node, nodeItem, reload]);
|
||||
|
||||
const CustomHeader = () => {
|
||||
const canDrop = evalFunc(node, node.canDrop, itemNodeData, item, treeNodeInfo);
|
||||
const canDropCascade = evalFunc(node, node.canDropCascade, itemNodeData, item, treeNodeInfo);
|
||||
const canDropForce = evalFunc(node, node.canDropForce, itemNodeData, item, treeNodeInfo);
|
||||
const canDrop = evalFunc(node, node.canDrop, nodeData, nodeItem, treeNodeInfo);
|
||||
const canDropCascade = evalFunc(node, node.canDropCascade, nodeData, nodeItem, treeNodeInfo);
|
||||
const canDropForce = evalFunc(node, node.canDropForce, nodeData, nodeItem, treeNodeInfo);
|
||||
return (
|
||||
<Box >
|
||||
<PgIconButton
|
||||
@ -337,7 +343,7 @@ export function CollectionNodeView({
|
||||
{data.length > 0 ?
|
||||
(
|
||||
<PgTable
|
||||
isSelectRow={!('catalog' in treeNodeInfo) && (itemNodeData.label !== 'Catalogs') && _.isUndefined(node?.canSelect)}
|
||||
isSelectRow={!('catalog' in treeNodeInfo) && (nodeData.label !== 'Catalogs') && _.isUndefined(node?.canSelect)}
|
||||
CustomHeader={CustomHeader}
|
||||
className={classes.autoResizer}
|
||||
columns={pgTableColumns}
|
||||
@ -361,17 +367,19 @@ export function CollectionNodeView({
|
||||
);
|
||||
}
|
||||
|
||||
CollectionNodeView.propTypes = {
|
||||
CollectionNodeProperties.propTypes = {
|
||||
node: PropTypes.func,
|
||||
itemData: PropTypes.object,
|
||||
itemNodeData: PropTypes.object,
|
||||
nodeData: PropTypes.object,
|
||||
treeNodeInfo: PropTypes.object,
|
||||
item: PropTypes.object,
|
||||
pgBrowser: PropTypes.object,
|
||||
nodeItem: PropTypes.object,
|
||||
preferences: PropTypes.object,
|
||||
sid: PropTypes.number,
|
||||
did: PropTypes.number,
|
||||
row: PropTypes.object,
|
||||
serverConnected: PropTypes.bool,
|
||||
value: PropTypes.bool,
|
||||
isActive: PropTypes.bool,
|
||||
isStale: PropTypes.bool,
|
||||
setIsStale: PropTypes.func,
|
||||
};
|
||||
|
247
web/pgadmin/misc/properties/ObjectNodeProperties.jsx
Normal file
247
web/pgadmin/misc/properties/ObjectNodeProperties.jsx
Normal file
@ -0,0 +1,247 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import {getHelpUrl, getEPASHelpUrl} from 'pgadmin.help';
|
||||
import SchemaView from 'sources/SchemaView';
|
||||
import gettext from 'sources/gettext';
|
||||
import { generateNodeUrl } from '../../browser/static/js/node_ajax';
|
||||
import { usePgAdmin } from '../../static/js/BrowserComponent';
|
||||
import { LAYOUT_EVENTS, LayoutDockerContext } from '../../static/js/helpers/Layout';
|
||||
import usePreferences from '../../preferences/static/js/store';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function ObjectNodeProperties({panelId, node, treeNodeInfo, nodeData, actionType, formType, onEdit, onSave, onClose,
|
||||
isActive, isStale, setIsStale}) {
|
||||
const layoutDocker = React.useContext(LayoutDockerContext);
|
||||
const nodeType = nodeData?._type;
|
||||
const pgAdmin = usePgAdmin();
|
||||
let serverInfo = treeNodeInfo && ('server' in treeNodeInfo) &&
|
||||
pgAdmin.Browser.serverInfo && pgAdmin.Browser.serverInfo[treeNodeInfo.server._id];
|
||||
let inCatalog = treeNodeInfo && ('catalog' in treeNodeInfo);
|
||||
let urlBase = generateNodeUrl.call(node, treeNodeInfo, actionType, nodeData, false, node.url_jump_after_node);
|
||||
const api = getApiInstance();
|
||||
const url = (isNew)=>{
|
||||
return urlBase + (isNew ? '' : nodeData._id);
|
||||
};
|
||||
const isDirty = useRef(false); // usefull for warnings
|
||||
let warnOnCloseFlag = true;
|
||||
const confirmOnCloseReset = usePreferences().getPreferencesForModule('browser').confirm_on_properties_close;
|
||||
let updatedData = ['table', 'partition'].includes(nodeType) && !_.isEmpty(nodeData.rows_cnt) ? {rows_cnt: nodeData.rows_cnt} : undefined;
|
||||
|
||||
let onError = (err)=> {
|
||||
if(err.response){
|
||||
console.error('error resp', err.response);
|
||||
} else if(err.request){
|
||||
console.error('error req', err.request);
|
||||
} else if(err.message){
|
||||
console.error('error msg', err.message);
|
||||
}
|
||||
};
|
||||
|
||||
/* Called when dialog is opened in edit mode, promise required */
|
||||
let initData = ()=>new Promise((resolve, reject)=>{
|
||||
if(actionType === 'create') {
|
||||
resolve({});
|
||||
} else {
|
||||
// Do not call the API if tab is not active.
|
||||
if(!isActive && actionType == 'properties') {
|
||||
return;
|
||||
}
|
||||
api.get(url(false))
|
||||
.then((res)=>{
|
||||
resolve(res.data);
|
||||
})
|
||||
.catch((err)=>{
|
||||
pgAdmin.Browser.notifier.pgNotifier('error', err, '', function(msg) {
|
||||
if (msg == 'CRYPTKEY_SET') {
|
||||
return Promise.resolve(initData());
|
||||
} else if (msg == 'CRYPTKEY_NOT_SET') {
|
||||
reject(gettext('The master password is not set.'));
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
|
||||
})
|
||||
.then(()=>{
|
||||
// applies only for properties tab
|
||||
setIsStale?.(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* on save button callback, promise required */
|
||||
const onSaveClick = (isNew, data)=>new Promise((resolve, reject)=>{
|
||||
return api({
|
||||
url: url(isNew),
|
||||
method: isNew ? 'POST' : 'PUT',
|
||||
data: data,
|
||||
}).then((res)=>{
|
||||
/* Don't warn the user before closing dialog */
|
||||
warnOnCloseFlag = false;
|
||||
resolve(res.data);
|
||||
onSave?.(res.data);
|
||||
}).catch((err)=>{
|
||||
pgAdmin.Browser.notifier.pgNotifier('error-noalert', err, '', function(msg) {
|
||||
if (msg == 'CRYPTKEY_SET') {
|
||||
return Promise.resolve(onSaveClick(isNew, data));
|
||||
} else if (msg == 'CRYPTKEY_NOT_SET') {
|
||||
reject(gettext('The master password is not set.'));
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/* Called when switched to SQL tab, promise required */
|
||||
const getSQLValue = (isNew, changedData)=>{
|
||||
const msqlUrl = generateNodeUrl.call(node, treeNodeInfo, 'msql', nodeData, !isNew, node.url_jump_after_node);
|
||||
return new Promise((resolve, reject)=>{
|
||||
api({
|
||||
url: msqlUrl,
|
||||
method: 'GET',
|
||||
params: changedData,
|
||||
}).then((res)=>{
|
||||
resolve(res.data.data);
|
||||
}).catch((err)=>{
|
||||
onError(err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/* Callback for help button */
|
||||
const onHelp = (isSqlHelp=false, isNew=false)=>{
|
||||
if(isSqlHelp) {
|
||||
let server = treeNodeInfo.server;
|
||||
let helpUrl = pgAdmin.Browser.utils.pg_help_path;
|
||||
let fullUrl = '';
|
||||
|
||||
if (server.server_type == 'ppas' && node.epasHelp) {
|
||||
fullUrl = getEPASHelpUrl(server.version);
|
||||
} else {
|
||||
if (node.sqlCreateHelp == '' && node.sqlAlterHelp != '') {
|
||||
fullUrl = getHelpUrl(helpUrl, node.sqlAlterHelp, server.version);
|
||||
} else if (node.sqlCreateHelp != '' && node.sqlAlterHelp == '') {
|
||||
fullUrl = getHelpUrl(helpUrl, node.sqlCreateHelp, server.version);
|
||||
} else {
|
||||
if (isNew) {
|
||||
fullUrl = getHelpUrl(helpUrl, node.sqlCreateHelp, server.version);
|
||||
} else {
|
||||
fullUrl = getHelpUrl(helpUrl, node.sqlAlterHelp, server.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.open(fullUrl, 'postgres_help');
|
||||
} else {
|
||||
window.open(node.dialogHelp, 'pgadmin_help');
|
||||
}
|
||||
};
|
||||
|
||||
/* A warning before closing the dialog with unsaved changes, based on preference */
|
||||
const warnBeforeChangesLost = (id)=>{
|
||||
if(panelId != id) {
|
||||
warnBeforeChangesLost();
|
||||
}
|
||||
if (warnOnCloseFlag && confirmOnCloseReset) {
|
||||
if(isDirty.current) {
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Warning'),
|
||||
gettext('Changes will be lost. Are you sure you want to close the dialog?'),
|
||||
function() {
|
||||
onClose(true);
|
||||
},
|
||||
null
|
||||
);
|
||||
} else {
|
||||
onClose(true);
|
||||
}
|
||||
} else {
|
||||
onClose(true);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
if(formType == 'dialog') {
|
||||
/* Bind the close event and check if user should be warned */
|
||||
layoutDocker.eventBus.registerListener(LAYOUT_EVENTS.CLOSING, warnBeforeChangesLost);
|
||||
}
|
||||
return ()=>{
|
||||
layoutDocker.eventBus.deregisterListener(LAYOUT_EVENTS.CLOSING, warnBeforeChangesLost);
|
||||
};
|
||||
}, []);
|
||||
|
||||
/* All other useful details can go with this object */
|
||||
const viewHelperProps = {
|
||||
mode: actionType,
|
||||
serverInfo: serverInfo ? {
|
||||
type: serverInfo.server_type,
|
||||
version: serverInfo.version,
|
||||
}: undefined,
|
||||
inCatalog: inCatalog,
|
||||
};
|
||||
|
||||
let schema = node.getSchema.call(node, treeNodeInfo, nodeData);
|
||||
// Show/Hide security group for nodes under the catalog
|
||||
if('catalog' in treeNodeInfo
|
||||
&& formType !== 'tab') {
|
||||
schema.filterGroups = [gettext('Security')];
|
||||
}
|
||||
|
||||
const key = useMemo(()=>{
|
||||
if(!isActive && isStale || actionType != 'properties') {
|
||||
return nodeData?._id;
|
||||
} else if(isActive && isStale) {
|
||||
return nodeData?._id + '0';
|
||||
}
|
||||
return nodeData?._id + '1';
|
||||
}, [isActive, isStale, nodeData?._id]);
|
||||
|
||||
/* Fire at will, mount the DOM */
|
||||
return (
|
||||
<SchemaView
|
||||
key={key}
|
||||
formType={formType}
|
||||
getInitData={initData}
|
||||
updatedData={updatedData}
|
||||
schema={schema}
|
||||
viewHelperProps={viewHelperProps}
|
||||
onSave={onSaveClick}
|
||||
onClose={()=>onClose()}
|
||||
onHelp={onHelp}
|
||||
onEdit={onEdit}
|
||||
onDataChange={(dataChanged)=>{
|
||||
isDirty.current = dataChanged;
|
||||
}}
|
||||
confirmOnCloseReset={confirmOnCloseReset}
|
||||
hasSQL={node.hasSQL && (actionType === 'create' || actionType === 'edit')}
|
||||
getSQLValue={getSQLValue}
|
||||
disableSqlHelp={node.sqlAlterHelp == '' && node.sqlCreateHelp == '' && !node.epasHelp}
|
||||
disableDialogHelp={node.dialogHelp == undefined || node.dialogHelp == ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
ObjectNodeProperties.propTypes = {
|
||||
panelId: PropTypes.string,
|
||||
node: PropTypes.func,
|
||||
treeNodeInfo: PropTypes.object,
|
||||
nodeData: PropTypes.object,
|
||||
actionType: PropTypes.string,
|
||||
formType: PropTypes.string,
|
||||
onEdit: PropTypes.func,
|
||||
onSave: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
isActive: PropTypes.bool,
|
||||
isStale: PropTypes.bool,
|
||||
setIsStale: PropTypes.func,
|
||||
};
|
74
web/pgadmin/misc/properties/Properties.jsx
Normal file
74
web/pgadmin/misc/properties/Properties.jsx
Normal file
@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import CollectionNodeProperties from './CollectionNodeProperties';
|
||||
import ErrorBoundary from '../../static/js/helpers/ErrorBoundary';
|
||||
import withStandardTabInfo from '../../static/js/helpers/withStandardTabInfo';
|
||||
import { BROWSER_PANELS } from '../../browser/static/js/constants';
|
||||
import ObjectNodeProperties from './ObjectNodeProperties';
|
||||
import EmptyPanelMessage from '../../static/js/components/EmptyPanelMessage';
|
||||
import gettext from 'sources/gettext';
|
||||
import { Box, makeStyles } from '@material-ui/core';
|
||||
import { usePgAdmin } from '../../static/js/BrowserComponent';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
height: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
}));
|
||||
|
||||
function Properties(props) {
|
||||
const isCollection = props.nodeData?._type?.startsWith('coll-');
|
||||
const classes = useStyles();
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
if(!props.node) {
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<Box margin={'4px auto'}>
|
||||
<EmptyPanelMessage text={gettext('Please select an object in the tree view.')} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if(isCollection) {
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<ErrorBoundary>
|
||||
<CollectionNodeProperties
|
||||
{...props}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<ErrorBoundary>
|
||||
<ObjectNodeProperties
|
||||
{...props}
|
||||
actionType='properties'
|
||||
formType="tab"
|
||||
onEdit={()=>{
|
||||
pgAdmin.Browser.Node.callbacks.show_obj_properties.call(
|
||||
props.node, {action: 'edit'}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Properties.propTypes = {
|
||||
node: PropTypes.func,
|
||||
treeNodeInfo: PropTypes.object,
|
||||
nodeData: PropTypes.object,
|
||||
nodeItem: PropTypes.object,
|
||||
};
|
||||
|
||||
export default withStandardTabInfo(Properties, BROWSER_PANELS.PROPERTIES);
|
@ -11,11 +11,13 @@ import React, { useEffect } from 'react';
|
||||
import { generateNodeUrl } from '../../../../browser/static/js/node_ajax';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import Notify from '../../../../static/js/helpers/Notifier';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import CodeMirror from '../../../../static/js/components/CodeMirror';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import withStandardTabInfo from '../../../../static/js/helpers/withStandardTabInfo';
|
||||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
textArea: {
|
||||
@ -28,26 +30,29 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function SQL({ nodeData, node, did, ...props }) {
|
||||
function SQL({nodeData, node, treeNodeInfo, isActive, isStale, setIsStale}) {
|
||||
const classes = useStyles();
|
||||
const did = ((!_.isUndefined(treeNodeInfo)) && (!_.isUndefined(treeNodeInfo['database']))) ? treeNodeInfo['database']._id: 0;
|
||||
const dbConnected = !_.isUndefined(treeNodeInfo) && !_.isUndefined(treeNodeInfo['database']) ? treeNodeInfo.database.connected: false;
|
||||
const [nodeSQL, setNodeSQL] = React.useState('');
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
const [msg, setMsg] = React.useState('');
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
useEffect(() => {
|
||||
if(!isStale || !isActive) {
|
||||
return;
|
||||
}
|
||||
let sql = '-- ' + gettext('Please select an object in the tree view.');
|
||||
if (node) {
|
||||
if(node) {
|
||||
let url = generateNodeUrl.call(
|
||||
node,
|
||||
props.treeNodeInfo,
|
||||
treeNodeInfo,
|
||||
'sql',
|
||||
nodeData,
|
||||
true,
|
||||
node.url_jump_after_node
|
||||
);
|
||||
setLoaderText('Loading...');
|
||||
if (did && !props.dbConnected){
|
||||
setLoaderText('');
|
||||
if (did && !dbConnected){
|
||||
return;
|
||||
}
|
||||
sql =
|
||||
@ -55,6 +60,7 @@ export default function SQL({ nodeData, node, did, ...props }) {
|
||||
|
||||
if (node.hasSQL) {
|
||||
const api = getApiInstance();
|
||||
setLoaderText('Loading...');
|
||||
api({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
@ -64,37 +70,34 @@ export default function SQL({ nodeData, node, did, ...props }) {
|
||||
setNodeSQL(res.data);
|
||||
setLoaderText('');
|
||||
} else {
|
||||
setMsg(sql);
|
||||
setNodeSQL(sql);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Error'),
|
||||
gettext(e.response.data.errormsg)
|
||||
);
|
||||
// show failed message.
|
||||
setMsg(gettext('Failed to retrieve data from the server.'));
|
||||
setNodeSQL([gettext('Failed to retrieve data from the server.'), true]);
|
||||
setLoaderText('');
|
||||
}).then(()=>{
|
||||
setLoaderText('');
|
||||
});
|
||||
}else{
|
||||
setMsg(sql);
|
||||
setLoaderText('');
|
||||
}
|
||||
}
|
||||
if (sql != '') {
|
||||
setMsg(sql);
|
||||
setNodeSQL(sql);
|
||||
}
|
||||
return () => {
|
||||
setNodeSQL([]);
|
||||
};
|
||||
}, [nodeData, props.dbConnected]);
|
||||
setIsStale(false);
|
||||
}, [isStale, isActive]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Loader message={loaderText}/>
|
||||
<CodeMirror
|
||||
className={classes.textArea}
|
||||
value={nodeSQL.length > 0 ? nodeSQL : msg}
|
||||
value={nodeSQL}
|
||||
readonly={true}
|
||||
options={{
|
||||
lineNumbers: true,
|
||||
@ -111,5 +114,10 @@ SQL.propTypes = {
|
||||
treeNodeInfo: PropTypes.object,
|
||||
node: PropTypes.func,
|
||||
dbConnected: PropTypes.bool,
|
||||
did: PropTypes.number
|
||||
did: PropTypes.number,
|
||||
isActive: PropTypes.bool,
|
||||
isStale: PropTypes.bool,
|
||||
setIsStale: PropTypes.func,
|
||||
};
|
||||
|
||||
export default withStandardTabInfo(SQL, BROWSER_PANELS.SQL);
|
||||
|
@ -12,13 +12,15 @@ import React, { useEffect } from 'react';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import Notify from '../../../../static/js/helpers/Notifier';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { getURL } from '../../../static/utils/utils';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import EmptyPanelMessage from '../../../../static/js/components/EmptyPanelMessage';
|
||||
import { compareSizeVals, toPrettySize } from '../../../../static/js/utils';
|
||||
import withStandardTabInfo from '../../../../static/js/helpers/withStandardTabInfo';
|
||||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
@ -155,7 +157,8 @@ function createSingleLineStatistics(data, prettifyFields) {
|
||||
return res;
|
||||
}
|
||||
|
||||
export default function Statistics({ nodeData, item, node, ...props }) {
|
||||
// {nodeData, node, treeNodeInfo, isActive, isStale, setIsStale}
|
||||
function Statistics({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale, setIsStale }) {
|
||||
const classes = useStyles();
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
|
||||
@ -177,13 +180,18 @@ export default function Statistics({ nodeData, item, node, ...props }) {
|
||||
disableGlobalFilter: false,
|
||||
},
|
||||
]);
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
useEffect(() => {
|
||||
if(!isStale || !isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
let url,
|
||||
message = gettext('Please select an object in the tree view.');
|
||||
|
||||
if (node) {
|
||||
url = getURL(nodeData, true, props.treeNodeInfo, node, item, 'stats');
|
||||
url = getURL(nodeData, true, treeNodeInfo, node, nodeItem, 'stats');
|
||||
|
||||
message = gettext('No statistics are available for the selected object.');
|
||||
|
||||
@ -207,7 +215,7 @@ export default function Statistics({ nodeData, item, node, ...props }) {
|
||||
setLoaderText('');
|
||||
|
||||
if (err?.response?.data?.info == 'CRYPTKEY_MISSING') {
|
||||
Notify.pgNotifier('error', err.request, 'The master password is not set', function(mesg) {
|
||||
pgAdmin.Browser.notifier.pgNotifier('error', err.request, 'The master password is not set', function(mesg) {
|
||||
setTimeout(function() {
|
||||
if (mesg == 'CRYPTKEY_SET') {
|
||||
setMsg('No statistics are available for the selected object.');
|
||||
@ -217,7 +225,7 @@ export default function Statistics({ nodeData, item, node, ...props }) {
|
||||
}, 100);
|
||||
});
|
||||
} else {
|
||||
Notify.alert(
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
gettext('Failed to retrieve data from the server.'),
|
||||
gettext(err.message)
|
||||
);
|
||||
@ -232,10 +240,8 @@ export default function Statistics({ nodeData, item, node, ...props }) {
|
||||
if (message != '') {
|
||||
setMsg(message);
|
||||
}
|
||||
return () => {
|
||||
setTableData([]);
|
||||
};
|
||||
}, [nodeData]);
|
||||
setIsStale(false);
|
||||
}, [isStale, isActive]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -260,7 +266,12 @@ export default function Statistics({ nodeData, item, node, ...props }) {
|
||||
Statistics.propTypes = {
|
||||
res: PropTypes.array,
|
||||
nodeData: PropTypes.object,
|
||||
item: PropTypes.object,
|
||||
nodeItem: PropTypes.object,
|
||||
treeNodeInfo: PropTypes.object,
|
||||
node: PropTypes.func,
|
||||
isActive: PropTypes.bool,
|
||||
isStale: PropTypes.bool,
|
||||
setIsStale: PropTypes.func,
|
||||
};
|
||||
|
||||
export default withStandardTabInfo(Statistics, BROWSER_PANELS.STATISTICS);
|
||||
|
@ -21,13 +21,12 @@ import CloseSharpIcon from '@material-ui/icons/CloseSharp';
|
||||
import HelpIcon from '@material-ui/icons/HelpRounded';
|
||||
import SaveSharpIcon from '@material-ui/icons/SaveSharp';
|
||||
import clsx from 'clsx';
|
||||
import Notify from '../../../../static/js/helpers/Notifier';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import { DefaultButton, PgIconButton, PrimaryButton } from '../../../../static/js/components/Buttons';
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import { getBinaryPathSchema } from '../../../../browser/server_groups/servers/static/js/binary_path.ui';
|
||||
import { _set_dynamic_tab } from '../../../../tools/sqleditor/static/js/show_query_tool';
|
||||
import { getBrowserAccesskey } from '../../../../static/js/components/ShortcutTitle';
|
||||
import usePreferences from '../store';
|
||||
|
||||
class PreferencesSchema extends BaseUISchema {
|
||||
constructor(initValues = {}, schemaFields = []) {
|
||||
@ -168,6 +167,7 @@ export default function PreferencesComponent({ ...props }) {
|
||||
const [loadTree, setLoadTree] = React.useState(0);
|
||||
const api = getApiInstance();
|
||||
const firstTreeElement = React.useRef('');
|
||||
const preferencesStore = usePreferences();
|
||||
|
||||
useEffect(() => {
|
||||
const pref_url = url_for('preferences.index');
|
||||
@ -230,7 +230,7 @@ export default function PreferencesComponent({ ...props }) {
|
||||
// set Preferences schema
|
||||
prefSchema.current = new PreferencesSchema(preferencesValues, preferencesData);
|
||||
}).catch((err) => {
|
||||
Notify.alert(err);
|
||||
pgAdmin.Browser.notifier.alert(err);
|
||||
});
|
||||
}, []);
|
||||
function setPreferences(node, subNode, nodeData, preferencesValues, preferencesData) {
|
||||
@ -328,8 +328,8 @@ export default function PreferencesComponent({ ...props }) {
|
||||
element.canDelete = false;
|
||||
element.canEdit = false;
|
||||
element.editable = false;
|
||||
if (pgAdmin.Browser.get_preference(node.label.toLowerCase(), element.name)?.value) {
|
||||
let temp = pgAdmin.Browser.get_preference(node.label.toLowerCase(), element.name).value;
|
||||
if (preferencesStore.getPreferences(node.label.toLowerCase(), element.name)?.value) {
|
||||
let temp = preferencesStore.getPreferences(node.label.toLowerCase(), element.name).value;
|
||||
preferencesValues[element.id] = temp;
|
||||
} else {
|
||||
preferencesValues[element.id] = element.value;
|
||||
@ -547,44 +547,19 @@ export default function PreferencesComponent({ ...props }) {
|
||||
return s.name=='show_system_objects'||s.name=='show_empty_coll_nodes'||s.name.startsWith('show_node_')||s.name=='hide_shared_server'||s.name=='show_user_defined_templates';
|
||||
});
|
||||
let requires_refresh = false;
|
||||
/* Find the modules changed */
|
||||
let modulesChanged = {};
|
||||
for (const [key] of Object.entries(data.current)) {
|
||||
let pref = pgAdmin.Browser.get_preference_for_id(Number(key));
|
||||
|
||||
if (pref['name'] == 'dynamic_tabs') {
|
||||
_set_dynamic_tab(pgAdmin.Browser, !pref['value']);
|
||||
}
|
||||
|
||||
if (!modulesChanged[pref.module]) {
|
||||
modulesChanged[pref.module] = true;
|
||||
}
|
||||
|
||||
let pref = preferencesStore.getPreferenceForId(Number(key));
|
||||
requires_refresh = checkRefreshRequired(pref, requires_refresh);
|
||||
|
||||
// Sync the lock layout menu with preferences
|
||||
if (pref.name == 'lock_layout') {
|
||||
let fileMenu = pgAdmin.Browser.MainMenus.find((menu) => menu.name == 'file');
|
||||
let layoutSubMenu = fileMenu['menuItems'].find(menu => menu.name == 'mnu_locklayout');
|
||||
layoutSubMenu['menu_items'].forEach(item => {
|
||||
if (item.name === 'mnu_lock_'+save_data[0]['value']) {
|
||||
item.checked = true;
|
||||
} else {
|
||||
item.checked = false;
|
||||
}
|
||||
});
|
||||
pgAdmin.Browser.Events.trigger('pgadmin:nw-refresh-menu-item', 'lock_layout');
|
||||
}
|
||||
}
|
||||
|
||||
if (requiresTreeRefresh) {
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Object explorer refresh required'),
|
||||
gettext('An object explorer refresh is required. Do you wish to refresh it now?'),
|
||||
function () {
|
||||
pgAdmin.Browser.tree.destroy({
|
||||
success: function () {
|
||||
pgAdmin.Browser.initializeBrowserTree(pgAdmin.Browser);
|
||||
// pgAdmin.Browser.initializeBrowserTree(pgAdmin.Browser);
|
||||
return true;
|
||||
},
|
||||
});
|
||||
@ -598,7 +573,7 @@ export default function PreferencesComponent({ ...props }) {
|
||||
}
|
||||
|
||||
if (requires_refresh) {
|
||||
Notify.confirm(
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Refresh required'),
|
||||
gettext('A page refresh is required to apply the theme. Do you wish to refresh the page now?'),
|
||||
function () {
|
||||
@ -612,10 +587,10 @@ export default function PreferencesComponent({ ...props }) {
|
||||
);
|
||||
}
|
||||
// Refresh preferences cache
|
||||
pgAdmin.Browser.cache_preferences(modulesChanged);
|
||||
preferencesStore.cache();
|
||||
props.closeModal();
|
||||
}).catch((err) => {
|
||||
Notify.alert(err.response.data);
|
||||
pgAdmin.Browser.notifier.alert(err.response.data);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,8 @@
|
||||
import React from 'react';
|
||||
import gettext from 'sources/gettext';
|
||||
import PreferencesComponent from './components/PreferencesComponent';
|
||||
import Notify from '../../../static/js/helpers/Notifier';
|
||||
import PreferencesTree from './components/PreferencesTree';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
export default class Preferences {
|
||||
static instance;
|
||||
@ -50,7 +50,7 @@ export default class Preferences {
|
||||
show() {
|
||||
|
||||
// Render Preferences component
|
||||
Notify.showModal(gettext('Preferences'), (closeModal) => {
|
||||
pgAdmin.Browser.notifier.showModal(gettext('Preferences'), (closeModal) => {
|
||||
return <PreferencesComponent
|
||||
renderTree={(prefTreeData) => {
|
||||
// Render preferences tree component
|
||||
|
81
web/pgadmin/preferences/static/js/store.js
Normal file
81
web/pgadmin/preferences/static/js/store.js
Normal file
@ -0,0 +1,81 @@
|
||||
import {create} from 'zustand';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import url_for from 'sources/url_for';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
const usePreferences = create((set, get)=>({
|
||||
data: {},
|
||||
version: 0,
|
||||
isLoading: true,
|
||||
failed: false,
|
||||
getPreferences: (module, preference)=>{
|
||||
return _.find(
|
||||
get().data, {'module': module, 'name': preference}
|
||||
);
|
||||
},
|
||||
getPreferencesForModule: function(module) {
|
||||
let preferences = {};
|
||||
_.forEach(
|
||||
_.filter(get().data, {'module': module}),
|
||||
(preference) => {
|
||||
preferences[preference.name] = preference.value;
|
||||
}
|
||||
);
|
||||
return preferences;
|
||||
},
|
||||
/* Get preference of an id, id is numeric */
|
||||
getPreferenceForId : function(id) {
|
||||
return _.find(get().data, {'id': id});
|
||||
},
|
||||
cache: async ()=>{
|
||||
try {
|
||||
const res = await getApiInstance().get(url_for('preferences.get_all'));
|
||||
set({data: res.data, version: (new Date()).getTime(), isLoading: false});
|
||||
} catch (error) {
|
||||
set({data: {}, version: (new Date()).getTime(), isLoading: false, failed: true});
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
export default usePreferences;
|
||||
|
||||
// Setup two way broadcast channel
|
||||
// This will help to sync preferences in iframes/tabs of Query tool, debugger, etc.
|
||||
const preferenceChangeBroadcast = new BroadcastChannel('preference-change');
|
||||
|
||||
export function setupPreferenceBroadcast() {
|
||||
const broadcast = (state)=>{
|
||||
preferenceChangeBroadcast.postMessage({
|
||||
data: state.data,
|
||||
version: state.version,
|
||||
});
|
||||
};
|
||||
|
||||
// broadcast when state changed.
|
||||
usePreferences.subscribe((state)=>{
|
||||
broadcast(state);
|
||||
});
|
||||
|
||||
// if asked for sync from a tab then broadcast once.
|
||||
preferenceChangeBroadcast.onmessage = (ev)=>{
|
||||
if(ev.data == 'sync') {
|
||||
broadcast(usePreferences.getState());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function listenPreferenceBroadcast() {
|
||||
preferenceChangeBroadcast.onmessage = (ev)=>{
|
||||
const currState = usePreferences.getState();
|
||||
if(currState.version < ev.data.version) {
|
||||
usePreferences.setState({
|
||||
...usePreferences.getState(),
|
||||
...ev.data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// initial sync
|
||||
preferenceChangeBroadcast.postMessage('sync');
|
||||
}
|
@ -7,13 +7,7 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import Notify from '../../../static/js/helpers/Notifier';
|
||||
import { getBrowser } from '../../../static/js/utils';
|
||||
|
||||
define('pgadmin.settings', [
|
||||
'sources/pgadmin', 'sources/gettext', 'sources/url_for',
|
||||
], function(pgAdmin, gettext, url_for) {
|
||||
define('pgadmin.settings', ['sources/pgadmin'], function(pgAdmin) {
|
||||
|
||||
// This defines the Preference/Options Dialog for pgAdmin IV.
|
||||
pgAdmin = pgAdmin || window.pgAdmin || {};
|
||||
@ -35,34 +29,7 @@ define('pgadmin.settings', [
|
||||
// We will force unload method to not to save current layout
|
||||
// and reload the window
|
||||
show: function() {
|
||||
Notify.confirm(gettext('Reset layout'),
|
||||
gettext('Are you sure you want to reset the current layout? This will cause the application to reload and any un-saved data will be lost.'),
|
||||
function() {
|
||||
const reloadingIndicator = document.createElement('div');
|
||||
reloadingIndicator.setAttribute('id', 'reloading-indicator');
|
||||
document.body.appendChild(reloadingIndicator);
|
||||
|
||||
// Delete the record from database as well, then only reload page
|
||||
getApiInstance().delete(url_for('settings.reset_layout'))
|
||||
.then(()=>{
|
||||
window.onbeforeunload = null;
|
||||
// Now reload page
|
||||
location.reload(true);
|
||||
let {name: browser} = getBrowser();
|
||||
if(browser == 'Nwjs') {
|
||||
pgAdmin.Browser.create_menus();
|
||||
}
|
||||
})
|
||||
.catch(()=>{
|
||||
console.warn(
|
||||
'Something went wrong on server while resetting layout.'
|
||||
);
|
||||
});
|
||||
},
|
||||
function() {
|
||||
// Do nothing as user canceled the operation.
|
||||
}
|
||||
);
|
||||
pgAdmin.Browser.docker.resetLayout();
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -9,9 +9,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import BrowserComponent from '../js/BrowserComponent';
|
||||
import MainMenuFactory from '../../browser/static/js/MainMenuFactory';
|
||||
import AppMenuBar from '../js/AppMenuBar';
|
||||
import ObjectBreadcrumbs from '../js/components/ObjectBreadcrumbs';
|
||||
import Theme from '../js/Theme';
|
||||
|
||||
define('app', [
|
||||
@ -51,18 +50,25 @@ define('app', [
|
||||
// Create menus after all modules are initialized.
|
||||
MainMenuFactory.createMainMenus();
|
||||
|
||||
const menuContainerEle = document.querySelector('#main-menu-container');
|
||||
if(menuContainerEle) {
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<AppMenuBar />
|
||||
</Theme>, menuContainerEle
|
||||
);
|
||||
}
|
||||
// const menuContainerEle = document.querySelector('#main-menu-container');
|
||||
// if(menuContainerEle) {
|
||||
// ReactDOM.render(
|
||||
// <Theme>
|
||||
// <AppMenuBar />
|
||||
// </Theme>, menuContainerEle
|
||||
// );
|
||||
// }
|
||||
|
||||
// ReactDOM.render(
|
||||
// <Theme>
|
||||
// <ObjectBreadcrumbs pgAdmin={pgAdmin} />
|
||||
// </Theme>, document.querySelector('#object-breadcrumbs')
|
||||
// );
|
||||
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<ObjectBreadcrumbs pgAdmin={pgAdmin} />
|
||||
</Theme>, document.querySelector('#object-breadcrumbs')
|
||||
<BrowserComponent pgAdmin={pgAdmin} />
|
||||
</Theme>,
|
||||
document.querySelector('#root')
|
||||
);
|
||||
});
|
||||
|
@ -13,7 +13,3 @@
|
||||
@import 'node_modules/@simonwep/pickr/dist/themes/monolith.min.css';
|
||||
|
||||
@import 'node_modules/uplot/dist/uPlot.min.css';
|
||||
|
||||
/* wcDocker dependencies */
|
||||
@import 'node_modules/webcabin-docker/Build/wcDocker.css';
|
||||
@import 'node_modules/jquery-contextmenu/dist/jquery.contextMenu.css';
|
||||
|
@ -1 +0,0 @@
|
||||
/* This dummy CSS required by wcdocker */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user