Replace pgAdmin NW.js container with Electron container. #7494
parent
4457a6a6a1
commit
91eb60a363
|
@ -9,6 +9,7 @@
|
|||
.DS_Store
|
||||
.idea
|
||||
.vscode
|
||||
.sonarlint
|
||||
.yarn
|
||||
/*.diff
|
||||
/*.patch
|
||||
|
@ -38,6 +39,7 @@ runtime/pgAdmin4.app/
|
|||
runtime/pgAdmin4.pro.user*
|
||||
runtime/pgAdmin4_resource.rc
|
||||
runtime/release/
|
||||
runtime/dev_config.json
|
||||
runtime/ui_BrowserWindow.h
|
||||
web/config_local.py
|
||||
web/pgadmin/misc/themes/pgadmin.themes.json
|
||||
|
|
56
Make.bat
56
Make.bat
|
@ -260,45 +260,32 @@ REM Main build sequence Ends
|
|||
RD /Q /S "%BUILDROOT%\docs\en_US\html\_sources" 1> nul 2>&1
|
||||
|
||||
ECHO Staging runtime components...
|
||||
XCOPY /S /I /E /H /Y "%WD%\runtime\assets" "%BUILDROOT%\runtime\assets" > nul || EXIT /B 1
|
||||
XCOPY /S /I /E /H /Y "%WD%\runtime\src" "%BUILDROOT%\runtime\src" > nul || EXIT /B 1
|
||||
MKDIR "%BUILDROOT%\runtime\resources\app"
|
||||
XCOPY /S /I /E /H /Y "%WD%\runtime\assets" "%BUILDROOT%\runtime\resources\app\assets" > nul || EXIT /B 1
|
||||
XCOPY /S /I /E /H /Y "%WD%\runtime\src" "%BUILDROOT%\runtime\resources\app\src" > nul || EXIT /B 1
|
||||
|
||||
COPY "%WD%\runtime\package.json" "%BUILDROOT%\runtime\" > nul || EXIT /B 1
|
||||
CD "%BUILDROOT%\runtime\"
|
||||
COPY "%WD%\runtime\package.json" "%BUILDROOT%\runtime\resources\app\" > nul || EXIT /B 1
|
||||
CD "%BUILDROOT%\runtime\resources\app\"
|
||||
CALL yarn install --production=true || EXIT /B 1
|
||||
|
||||
ECHO Downloading NWjs to %TMPDIR%...
|
||||
REM Get a fresh copy of nwjs.
|
||||
REM NOTE: The nw download servers seem to be very unreliable, so at the moment we're using wget which retries
|
||||
|
||||
REM YARN
|
||||
REM CALL yarn --cwd "%TMPDIR%" add nw || EXIT /B
|
||||
REM YARN END
|
||||
ECHO Downloading Electron to %TMPDIR%...
|
||||
REM Get a fresh copy of electron.
|
||||
|
||||
REM WGET
|
||||
REM Comment out the below line as the latest version having some
|
||||
REM problem https://github.com/nwjs/nw.js/issues/7964, so for the time being
|
||||
REM hardcoded the version to 0.77.0
|
||||
REM FOR /f "tokens=2 delims='" %%i IN ('yarn info nw ^| findstr "latest: "') DO SET "NW_VERSION=%%i"
|
||||
REM :GET_NW
|
||||
REM wget https://dl.nwjs.io/v%NW_VERSION%/nwjs-v%NW_VERSION%-win-x64.zip -O "%TMPDIR%\nwjs-v%NW_VERSION%-win-x64.zip"
|
||||
REM IF %ERRORLEVEL% NEQ 0 GOTO GET_NW
|
||||
FOR /f "tokens=2 delims='" %%i IN ('yarn info electron ^| findstr "latest: "') DO SET "ELECTRON_VERSION=%%i"
|
||||
:GET_NW
|
||||
wget https://github.com/electron/electron/releases/download/v%ELECTRON_VERSION%/electron-v%ELECTRON_VERSION%-win32-x64.zip -O "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64.zip"
|
||||
IF %ERRORLEVEL% NEQ 0 GOTO GET_NW
|
||||
|
||||
SET "NW_VERSION=0.77.0"
|
||||
wget https://dl.nwjs.io/v%NW_VERSION%/nwjs-v%NW_VERSION%-win-x64.zip -O "%TMPDIR%\nwjs-v%NW_VERSION%-win-x64.zip"
|
||||
|
||||
tar -C "%TMPDIR%" -xvf "%TMPDIR%\nwjs-v%NW_VERSION%-win-x64.zip" || EXIT /B 1
|
||||
MKDIR "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64" || EXIT /B 1
|
||||
tar -C "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64" -xvf "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64.zip" || EXIT /B 1
|
||||
REM WGET END
|
||||
|
||||
REM YARN
|
||||
REM XCOPY /S /I /E /H /Y "%TMPDIR%\node_modules\nw\nwjs\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
REM YARN END
|
||||
REM XCOPY
|
||||
XCOPY /S /I /E /H /Y "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
REM XCOPY END
|
||||
|
||||
REM WGET
|
||||
XCOPY /S /I /E /H /Y "%TMPDIR%\nwjs-v%NW_VERSION%-win-x64\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
REM WGET END
|
||||
|
||||
MOVE "%BUILDROOT%\runtime\nw.exe" "%BUILDROOT%\runtime\pgAdmin4.exe"
|
||||
MOVE "%BUILDROOT%\runtime\electron.exe" "%BUILDROOT%\runtime\pgAdmin4.exe"
|
||||
ECHO Attempting to sign the pgAdmin4.exe...
|
||||
CALL "%PGADMIN_SIGNTOOL_DIR%\signtool.exe" sign /tr http://timestamp.digicert.com "%BUILDROOT%\runtime\pgAdmin4.exe"
|
||||
IF %ERRORLEVEL% NEQ 0 (
|
||||
|
@ -310,8 +297,13 @@ REM Main build sequence Ends
|
|||
)
|
||||
|
||||
ECHO Replacing executable icon...
|
||||
CALL yarn --cwd "%TMPDIR%" add winresourcer || EXIT /B
|
||||
"%TMPDIR%\node_modules\winresourcer\bin\Resourcer.exe" -op:upd -src:"%BUILDROOT%\runtime\pgAdmin4.exe" -type:Icongroup -name:IDR_MAINFRAME -file:"%WD%\pkg\win32\Resources\pgAdmin4.ico"
|
||||
|
||||
ECHO Downloading rcedit.exe...
|
||||
wget https://github.com/electron/rcedit/releases/download/v2.0.0/rcedit-x64.exe -O "%TMPDIR%\rcedit-x64.exe"
|
||||
%TMPDIR%\rcedit-x64.exe "%BUILDROOT%\runtime\pgAdmin4.exe" --set-icon "%WD%\pkg\win32\Resources\pgAdmin4.ico"
|
||||
%TMPDIR%\rcedit-x64.exe "%BUILDROOT%\runtime\pgAdmin4.exe" --set-version-string "FileDescription" "%APP_NAME%"
|
||||
%TMPDIR%\rcedit-x64.exe "%BUILDROOT%\runtime\pgAdmin4.exe" --set-version-string "ProductName" "%APP_NAME%"
|
||||
%TMPDIR%\rcedit-x64.exe "%BUILDROOT%\runtime\pgAdmin4.exe" --set-product-version "%APP_VERSION%""
|
||||
|
||||
ECHO Staging PostgreSQL components...
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libpq.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
|
|
|
@ -133,55 +133,47 @@ _create_python_virtualenv() {
|
|||
_build_runtime() {
|
||||
echo "Assembling the desktop runtime..."
|
||||
|
||||
# Get a fresh copy of nwjs.
|
||||
# NOTE: The nw download servers seem to be very unreliable, so at the moment we're using wget
|
||||
# in a retry loop as Yarn/Npm don't seem to like that.
|
||||
# Get a fresh copy of electron.
|
||||
|
||||
# YARN:
|
||||
# yarn add --cwd "${BUILDROOT}" nw
|
||||
# YARN END
|
||||
ELECTRON_ARCH="x64"
|
||||
if [ "$(uname -m)" == "arm64" ]; then
|
||||
ELECTRON_ARCH="arm64"
|
||||
fi
|
||||
|
||||
# WGET:
|
||||
# Comment out the below line as the latest version having some
|
||||
# problem https://github.com/nwjs/nw.js/issues/7964, so for the time being
|
||||
# hardcoded the version to 0.77.0
|
||||
# NW_VERSION=$(yarn info nw | grep latest | awk -F "'" '{ print $2}')
|
||||
NW_VERSION="0.77.0"
|
||||
ELECTRON_VERSION=$(yarn info electron | grep latest | awk -F "'" '{ print $2}')
|
||||
|
||||
pushd "${BUILDROOT}" > /dev/null || exit
|
||||
while true;do
|
||||
wget "https://dl.nwjs.io/v${NW_VERSION}/nwjs-v${NW_VERSION}-linux-x64.tar.gz" && break
|
||||
rm "nwjs-v${NW_VERSION}-linux-x64.tar.gz"
|
||||
wget "https://github.com/electron/electron/releases/download/v${ELECTRON_VERSION}/electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}.zip" && break
|
||||
rm "electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}.zip"
|
||||
done
|
||||
tar -zxvf "nwjs-v${NW_VERSION}-linux-x64.tar.gz"
|
||||
unzip "electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}.zip" -d "electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}"
|
||||
popd > /dev/null || exit
|
||||
# WGET END
|
||||
|
||||
# Copy nwjs into the staging directory
|
||||
# Copy electron into the staging directory
|
||||
mkdir -p "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
|
||||
# The chmod command below is needed to fix the permission issue of
|
||||
# the NWjs binaries and files.
|
||||
# Change the permission for others and group the same as the owner
|
||||
chmod -R og=u "${BUILDROOT}/nwjs-v${NW_VERSION}-linux-x64"/*
|
||||
chmod -R og=u "${BUILDROOT}/electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}"/*
|
||||
# Explicitly remove write permissions for others and group
|
||||
chmod -R og-w "${BUILDROOT}/nwjs-v${NW_VERSION}-linux-x64"/*
|
||||
chmod -R og-w "${BUILDROOT}/electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}"/*
|
||||
|
||||
# YARN:
|
||||
# cp -r "${BUILDROOT}/node_modules/nw/nwjs"/* "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
# YARN END
|
||||
BUNDLEDIR="${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
|
||||
# WGET:
|
||||
cp -r "${BUILDROOT}/nwjs-v${NW_VERSION}-linux-x64"/* "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
cp -r "${BUILDROOT}/electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}"/* "${BUNDLEDIR}"
|
||||
# WGET END
|
||||
|
||||
mv "${DESKTOPROOT}/usr/${APP_NAME}/bin/nw" "${DESKTOPROOT}/usr/${APP_NAME}/bin/${APP_NAME}"
|
||||
mv "${BUNDLEDIR}/electron" "${BUNDLEDIR}/${APP_NAME}"
|
||||
|
||||
cp -r "${SOURCEDIR}/runtime/assets" "${DESKTOPROOT}/usr/${APP_NAME}/bin/assets"
|
||||
cp -r "${SOURCEDIR}/runtime/src" "${DESKTOPROOT}/usr/${APP_NAME}/bin/src"
|
||||
mkdir -p "${BUNDLEDIR}/resources/app"
|
||||
cp -r "${SOURCEDIR}/runtime/assets" "${BUNDLEDIR}/resources/app/assets"
|
||||
cp -r "${SOURCEDIR}/runtime/src" "${BUNDLEDIR}/resources/app/src"
|
||||
|
||||
cp "${SOURCEDIR}/runtime/package.json" "${DESKTOPROOT}/usr/${APP_NAME}/bin/"
|
||||
yarn --cwd "${DESKTOPROOT}/usr/${APP_NAME}/bin" install --production=true
|
||||
cp "${SOURCEDIR}/runtime/package.json" "${BUNDLEDIR}/resources/app"
|
||||
yarn --cwd "${BUNDLEDIR}/resources/app" install --production=true
|
||||
|
||||
# Create the icon
|
||||
mkdir -p "${DESKTOPROOT}/usr/share/icons/hicolor/128x128/apps/"
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>%APPID%</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>23A334</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx14.0</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1501</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>15A507</string>
|
||||
<key>LSEnvironment</key>
|
||||
<dict>
|
||||
<key>MallocNanoZone</key>
|
||||
<string>0</string>
|
||||
</dict>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>%APPVER%</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -26,56 +26,37 @@ _build_runtime() {
|
|||
echo "Assembling the runtime environment..."
|
||||
|
||||
test -d "${BUILD_ROOT}" || mkdir "${BUILD_ROOT}"
|
||||
|
||||
# Get a fresh copy of nwjs.
|
||||
# NOTE: The nw download servers seem to be very unreliable, so at the moment we're using wget
|
||||
# in a retry loop as Yarn/Npm don't seem to like that.
|
||||
|
||||
# Get a fresh copy of electron
|
||||
# uname -m returns "x86_64" on Intel, but we need "x64"
|
||||
NW_ARCH="x64"
|
||||
ELECTRON_ARCH="x64"
|
||||
if [ "$(uname -m)" == "arm64" ]; then
|
||||
NW_ARCH="arm64"
|
||||
ELECTRON_ARCH="arm64"
|
||||
fi
|
||||
|
||||
# YARN:
|
||||
# yarn add --cwd "${BUILDROOT}" nw
|
||||
# YARN END
|
||||
|
||||
# WGET:
|
||||
# Comment out the below line as the latest version having some
|
||||
# problem https://github.com/nwjs/nw.js/issues/7964, so for the time being
|
||||
# hardcoded the version to 0.77.0
|
||||
# NW_VERSION=$(yarn info nw | grep latest | awk -F "'" '{ print $2}')
|
||||
NW_VERSION="0.77.0"
|
||||
ELECTRON_VERSION=$(yarn info electron | grep latest | awk -F "'" '{ print $2}')
|
||||
|
||||
pushd "${BUILD_ROOT}" > /dev/null || exit
|
||||
while true;do
|
||||
wget "https://dl.nwjs.io/v${NW_VERSION}/nwjs-v${NW_VERSION}-osx-${NW_ARCH}.zip" && break
|
||||
rm "nwjs-v${NW_VERSION}-osx-${NW_ARCH}.zip"
|
||||
wget "https://github.com/electron/electron/releases/download/v${ELECTRON_VERSION}/electron-v${ELECTRON_VERSION}-darwin-${ELECTRON_ARCH}.zip" && break
|
||||
rm "electron-v${ELECTRON_VERSION}-darwin-${ELECTRON_ARCH}.zip"
|
||||
done
|
||||
unzip "nwjs-v${NW_VERSION}-osx-${NW_ARCH}.zip"
|
||||
unzip "electron-v${ELECTRON_VERSION}-darwin-${ELECTRON_ARCH}.zip"
|
||||
popd > /dev/null || exit
|
||||
# WGET END
|
||||
|
||||
# YARN:
|
||||
# cp -R "${BUILD_ROOT}/node_modules/nw/nwjs/nwjs.app" "${BUILD_ROOT}/"
|
||||
# YARN END
|
||||
|
||||
# WGET:
|
||||
cp -R "${BUILD_ROOT}/nwjs-v${NW_VERSION}-osx-${NW_ARCH}"/nwjs.app "${BUILD_ROOT}/"
|
||||
# WGET END
|
||||
|
||||
mv "${BUILD_ROOT}/nwjs.app" "${BUNDLE_DIR}"
|
||||
mv "${BUILD_ROOT}/Electron.app" "${BUNDLE_DIR}"
|
||||
find "${BUNDLE_DIR}" -exec touch {} \;
|
||||
|
||||
# Copy in the runtime code
|
||||
mkdir "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
cp -R "${SOURCE_DIR}/runtime/assets" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
cp -R "${SOURCE_DIR}/runtime/src" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
cp "${SOURCE_DIR}/runtime/package.json" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
cp "${SOURCE_DIR}/runtime/.yarnrc.yml" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
mkdir "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
cp -R "${SOURCE_DIR}/runtime/assets" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
cp -R "${SOURCE_DIR}/runtime/src" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
cp "${SOURCE_DIR}/runtime/package.json" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
cp "${SOURCE_DIR}/runtime/.yarnrc.yml" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
|
||||
# Install the runtime node_modules, then replace the package.json
|
||||
pushd "${BUNDLE_DIR}/Contents/Resources/app.nw/" > /dev/null || exit
|
||||
pushd "${BUNDLE_DIR}/Contents/Resources/app/" > /dev/null || exit
|
||||
yarn set version berry
|
||||
yarn set version 3
|
||||
yarn plugin import workspace-tools
|
||||
|
@ -178,7 +159,6 @@ _fixup_imports() {
|
|||
# Find all the files that may need tweaks
|
||||
TODO=$(find . -perm +0111 -type f -exec file "{}" \; | \
|
||||
grep -v "Frameworks/Python.framework" | \
|
||||
grep -v "Frameworks/nwjs" | \
|
||||
grep -E "Mach-O 64-bit" | \
|
||||
awk -F ':| ' '{ORS=" "; print $1}' | \
|
||||
uniq)
|
||||
|
@ -273,10 +253,19 @@ _complete_bundle() {
|
|||
sed -i '' "s/%APPNAME%/${APP_NAME}/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
||||
sed -i '' "s/%APPVER%/${APP_LONG_VERSION}/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
||||
sed -i '' "s/%APPID%/org.pgadmin.pgadmin4/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
||||
for FILE in "${BUNDLE_DIR}"/Contents/Resources/*.lproj/InfoPlist.strings; do
|
||||
sed -i '' 's/CFBundleGetInfoString =.*/CFBundleGetInfoString = "Copyright (C) 2013 - 2024, The pgAdmin Development Team";/g' "${FILE}"
|
||||
sed -i '' 's/NSHumanReadableCopyright =.*/NSHumanReadableCopyright = "Copyright (C) 2013 - 2024, The pgAdmin Development Team";/g' "${FILE}"
|
||||
echo CFBundleDisplayName = \""${APP_NAME}"\"\; >> "${FILE}"
|
||||
|
||||
# Rename helper execs and Update the plist
|
||||
for helper_exec in "Electron Helper" "Electron Helper (Renderer)" "Electron Helper (Plugin)" "Electron Helper (GPU)"
|
||||
do
|
||||
pgadmin_exec=${helper_exec//Electron/pgAdmin 4}
|
||||
mv "${BUNDLE_DIR}/Contents/Frameworks/${helper_exec}.app/Contents/MacOS/${helper_exec}" "${BUNDLE_DIR}/Contents/Frameworks/${helper_exec}.app/Contents/MacOS/${pgadmin_exec}"
|
||||
mv "${BUNDLE_DIR}/Contents/Frameworks/${helper_exec}.app" "${BUNDLE_DIR}/Contents/Frameworks/${pgadmin_exec}.app"
|
||||
|
||||
info_plist="${BUNDLE_DIR}/Contents/Frameworks/${pgadmin_exec}.app/Contents/Info.plist"
|
||||
cp Info.plist-helper.in "${info_plist}"
|
||||
sed -i '' "s/%APPNAME%/${pgadmin_exec}/g" "${info_plist}"
|
||||
sed -i '' "s/%APPVER%/${APP_LONG_VERSION}/g" "${info_plist}"
|
||||
sed -i '' "s/%APPID%/org.pgadmin.pgadmin4.helper/g" "${info_plist}"
|
||||
done
|
||||
|
||||
# PkgInfo
|
||||
|
@ -286,10 +275,10 @@ _complete_bundle() {
|
|||
cp pgAdmin4.icns "${BUNDLE_DIR}/Contents/Resources/app.icns"
|
||||
|
||||
# Rename the executable
|
||||
mv "${BUNDLE_DIR}/Contents/MacOS/nwjs" "${BUNDLE_DIR}/Contents/MacOS/${APP_NAME}"
|
||||
mv "${BUNDLE_DIR}/Contents/MacOS/Electron" "${BUNDLE_DIR}/Contents/MacOS/${APP_NAME}"
|
||||
|
||||
# Rename the app in package.json so the menu looks as it should
|
||||
sed -i '' "s/\"name\": \"pgadmin4\"/\"name\": \"${APP_NAME}\"/g" "${BUNDLE_DIR}/Contents/Resources/app.nw/package.json"
|
||||
sed -i '' "s/\"name\": \"pgadmin4\"/\"name\": \"${APP_NAME}\"/g" "${BUNDLE_DIR}/Contents/Resources/app/package.json"
|
||||
|
||||
# Import the dependencies, and rewrite any library references
|
||||
_fixup_imports "${BUNDLE_DIR}"
|
||||
|
|
|
@ -41,7 +41,6 @@ sshtunnel==0.*
|
|||
ldap3==2.*
|
||||
gssapi==1.8.*
|
||||
eventlet==0.36.1
|
||||
httpagentparser==1.9.*
|
||||
user-agents==2.2.0
|
||||
pywinpty==2.0.*; sys_platform=="win32"
|
||||
Authlib==1.3.*; python_version > '3.7'
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
generated
|
||||
node_modules
|
||||
vendor
|
||||
templates/
|
||||
templates\
|
||||
ycache
|
|
@ -6,47 +6,55 @@
|
|||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import globals from 'globals';
|
||||
import js from '@eslint/js';
|
||||
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'amd': true,
|
||||
},
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ['**/*.js'],
|
||||
ignores: [
|
||||
'generated',
|
||||
'node_modules',
|
||||
'vendor',
|
||||
'templates/',
|
||||
'templates\\',
|
||||
'ycache',
|
||||
],
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 2018,
|
||||
"sourceType": "module",
|
||||
languageOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.es2017,
|
||||
...globals.amd,
|
||||
'_': 'readonly',
|
||||
'module': 'readonly',
|
||||
'process': 'readonly',
|
||||
'platform': 'readonly',
|
||||
},
|
||||
'globals': {
|
||||
'_': true,
|
||||
'module': true,
|
||||
'process': true,
|
||||
'nw': true,
|
||||
'platform': true
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
2
|
||||
2,
|
||||
],
|
||||
'linebreak-style': 0,
|
||||
'quotes': [
|
||||
'error',
|
||||
'single'
|
||||
'single',
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'always'
|
||||
'always',
|
||||
],
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'always-multiline'
|
||||
'always-multiline',
|
||||
],
|
||||
'no-console': ["error", { allow: ["warn", "error"] }],
|
||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
||||
// We need to exclude below for RegEx case
|
||||
"no-useless-escape": 0,
|
||||
'no-useless-escape': 0,
|
||||
},
|
||||
};
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,37 +1,24 @@
|
|||
{
|
||||
"name": "pgadmin4",
|
||||
"description": "pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.",
|
||||
"main": "src/html/pgadmin.html",
|
||||
"repository": "https://github.com/postgres/pgadmin4.git",
|
||||
"author": "pgAdmin Development Team (https://www.pgadmin.org/)",
|
||||
"license": "PostgreSQL",
|
||||
"chromium-args": "--disable-popup-blocking --disable-gpu --disable-devtools",
|
||||
"user-agent": "Nwjs:%nwver-%osinfo-%chromium_ver",
|
||||
"nodejs": true,
|
||||
"window": {
|
||||
"width": 750,
|
||||
"height": 600,
|
||||
"toolbar": false,
|
||||
"fullscreen": false,
|
||||
"frame": false,
|
||||
"show": true,
|
||||
"position": "center",
|
||||
"always-on-top": true,
|
||||
"icon": "assets/pgAdmin4.png"
|
||||
"type": "module",
|
||||
"main": "src/js/pgadmin.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"linter": "yarn run eslint -c .eslintrc.js ."
|
||||
},
|
||||
"icons": {
|
||||
"256": "assets/pgAdmin4.png"
|
||||
"packageManager": "yarn@3.8.2",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.5.0",
|
||||
"electron": "^30.0.5",
|
||||
"eslint": "^9.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"bootstrap": "^4.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^9.5.0",
|
||||
"nw": "0.77.0"
|
||||
},
|
||||
"scripts": {
|
||||
"linter": "yarn eslint --no-eslintrc -c .eslintrc.js --ext .js ."
|
||||
},
|
||||
"packageManager": "yarn@3.8.2"
|
||||
"bootstrap": "^4.5.3",
|
||||
"electron-store": "^9.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>pgAdmin 4 Configuration</title>
|
||||
<title>pgAdmin 4 Runtime Configuration</title>
|
||||
<link rel="stylesheet" href="../css/pgadmin-desktop.css"/>
|
||||
<style>
|
||||
body, html {
|
||||
|
@ -76,6 +76,99 @@
|
|||
<button id="btnSave" type="submit" class="btn btn-primary" disabled>Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../js/configure.js"></script>
|
||||
<script type="text/javascript" >
|
||||
let configData;
|
||||
|
||||
async function checkConfiguration() {
|
||||
const portNo = configData.portNo;
|
||||
|
||||
if (document.getElementById('fixedPortCheck').checked && portNo !== document.getElementById('portNo').value) {
|
||||
let fixedPort = parseInt(document.getElementById('portNo').value);
|
||||
// get the available TCP port
|
||||
if(await window.electronUI.checkPortAvailable(fixedPort)) {
|
||||
saveConfiguration();
|
||||
} else {
|
||||
alert('The port specified is already in use. Please enter a free port number.');
|
||||
}
|
||||
} else {
|
||||
saveConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
async function saveConfiguration() {
|
||||
await window.electronUI.setConfigData({
|
||||
'fixedPort': document.getElementById('fixedPortCheck').checked,
|
||||
'portNo': parseInt(document.getElementById('portNo').value),
|
||||
'connectionTimeout': parseInt(document.getElementById('timeOut').value),
|
||||
'openDocsInBrowser': document.getElementById('openDocsInBrowser').checked
|
||||
});
|
||||
|
||||
document.getElementById('status-text').innerHTML = 'Configuration Saved';
|
||||
const result = await window.electronUI.showMessageBox({
|
||||
'type': 'question',
|
||||
'title': 'Confirmation',
|
||||
'message': "pgAdmin 4 must be restarted for changes to take effect.\n\n Do you want to quit the application?",
|
||||
'buttons': [
|
||||
'Yes',
|
||||
'No'
|
||||
]
|
||||
});
|
||||
if(result.response === 0) {
|
||||
window.electronUI.restartApp();
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
|
||||
function onCheckChange() {
|
||||
if (this.checked) {
|
||||
document.getElementById('portNo').removeAttribute('disabled');
|
||||
} else {
|
||||
document.getElementById('portNo').setAttribute('disabled', 'disabled');
|
||||
}
|
||||
|
||||
// Enable/Disable Save button
|
||||
enableDisableSaveButton();
|
||||
}
|
||||
|
||||
function enableDisableSaveButton() {
|
||||
if (configData['fixedPort'] !== document.getElementById('fixedPortCheck').checked ||
|
||||
configData['portNo'] != document.getElementById('portNo').value ||
|
||||
configData['connectionTimeout'] != document.getElementById('timeOut').value ||
|
||||
configData['openDocsInBrowser'] !== document.getElementById('openDocsInBrowser').checked) {
|
||||
document.getElementById('btnSave').removeAttribute('disabled');
|
||||
} else {
|
||||
document.getElementById('btnSave').setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = async function() {
|
||||
configData = await window.electronUI.getConfigData();
|
||||
document.getElementById('status-text').innerHTML = '';
|
||||
// Set the GUI value as per configuration.
|
||||
if (configData['fixedPort']) {
|
||||
document.getElementById('fixedPortCheck').checked = true;
|
||||
document.getElementById('portNo').disabled = false;
|
||||
} else {
|
||||
document.getElementById('fixedPortCheck').checked = false;
|
||||
document.getElementById('portNo').disabled = true;
|
||||
}
|
||||
document.getElementById('portNo').value = configData['portNo'];
|
||||
document.getElementById('timeOut').value = configData['connectionTimeout'];
|
||||
|
||||
if (configData['openDocsInBrowser']) {
|
||||
document.getElementById('openDocsInBrowser').checked = true;
|
||||
} else {
|
||||
document.getElementById('openDocsInBrowser').checked = false;
|
||||
}
|
||||
// Add event listeners
|
||||
document.getElementById('btnSave').addEventListener('click', checkConfiguration);
|
||||
document.getElementById('fixedPortCheck').addEventListener('change', onCheckChange);
|
||||
document.getElementById('portNo').addEventListener('change', enableDisableSaveButton);
|
||||
document.getElementById('timeOut').addEventListener('change', enableDisableSaveButton);
|
||||
document.getElementById('openDocsInBrowser').addEventListener('change', enableDisableSaveButton);
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -31,6 +31,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../js/server_error.js"></script>
|
||||
<script>
|
||||
window.onload = async function() {
|
||||
document.getElementById('server_error_label').innerHTML = 'The pgAdmin 4 server could not be contacted:';
|
||||
document.getElementById('server_error_log').innerHTML = await window.electronUI.readServerLog();
|
||||
document.getElementById('btnConfigure').addEventListener('click', function() {
|
||||
window.electronUI.openConfigure();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
background-size: contain;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.loader-text {
|
||||
|
@ -50,9 +51,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="bg">
|
||||
<span id="loader-text-status" class="loader-text"></span>
|
||||
<span id="loader-text-status" class="loader-text">Waiting for pgAdmin 4 to start...</span>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../js/pgadmin.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -31,6 +31,19 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../js/view_log.js"></script>
|
||||
<script>
|
||||
window.onload = async function() {
|
||||
document.getElementById('status-text').innerHTML = '';
|
||||
document.getElementById('server_log_label').innerHTML = 'Server Log: ' + '(' + await window.electronUI.getServerLogFile() + ')';
|
||||
document.getElementById('server_log').innerHTML = await window.electronUI.readServerLog();
|
||||
document.getElementById('btnReload').addEventListener('click', function() {
|
||||
document.getElementById('server_log').innerHTML = 'Loading logs...';
|
||||
setTimeout(async function() {
|
||||
document.getElementById('server_log').innerHTML = await window.electronUI.readServerLog();
|
||||
}, 500);
|
||||
document.getElementById('status-text').innerHTML = 'Logs reloaded successfully';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
const misc = require('../js/misc.js');
|
||||
|
||||
// Get the window object of view log window
|
||||
let gui = require('nw.gui');
|
||||
let configWindow = gui.Window.get();
|
||||
|
||||
function checkConfiguration() {
|
||||
let configData = misc.ConfigureStore.getConfigData();
|
||||
|
||||
if (document.getElementById('fixedPortCheck').checked && configData['portNo'] !== document.getElementById('portNo').value) {
|
||||
let fixedPort = parseInt(document.getElementById('portNo').value);
|
||||
// get the available TCP port
|
||||
misc.getAvailablePort(fixedPort)
|
||||
.then(() => {
|
||||
saveConfiguration();
|
||||
})
|
||||
.catch(() => {
|
||||
alert('The port specified is already in use. Please enter a free port number.');
|
||||
});
|
||||
} else {
|
||||
saveConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
function saveConfiguration() {
|
||||
misc.ConfigureStore.set('fixedPort', document.getElementById('fixedPortCheck').checked);
|
||||
misc.ConfigureStore.set('portNo', parseInt(document.getElementById('portNo').value));
|
||||
misc.ConfigureStore.set('connectionTimeout', parseInt(document.getElementById('timeOut').value));
|
||||
misc.ConfigureStore.set('openDocsInBrowser', document.getElementById('openDocsInBrowser').checked);
|
||||
|
||||
misc.ConfigureStore.saveConfig();
|
||||
|
||||
document.getElementById('status-text').innerHTML = 'Configuration Saved';
|
||||
|
||||
if (confirm('pgAdmin 4 must be restarted for changes to take effect.\n\n Do you want to quit the application?') === true) {
|
||||
misc.cleanupAndQuitApp();
|
||||
}
|
||||
configWindow.close();
|
||||
}
|
||||
|
||||
function onCheckChange() {
|
||||
if (this.checked) {
|
||||
document.getElementById('portNo').removeAttribute('disabled');
|
||||
} else {
|
||||
document.getElementById('portNo').setAttribute('disabled', 'disabled');
|
||||
}
|
||||
|
||||
// Enable/Disable Save button
|
||||
enableDisableSaveButton();
|
||||
}
|
||||
|
||||
function enableDisableSaveButton() {
|
||||
let configData = misc.ConfigureStore.getConfigData();
|
||||
|
||||
if (configData['fixedPort'] !== document.getElementById('fixedPortCheck').checked ||
|
||||
configData['portNo'] != document.getElementById('portNo').value ||
|
||||
configData['connectionTimeout'] != document.getElementById('timeOut').value ||
|
||||
configData['openDocsInBrowser'] !== document.getElementById('openDocsInBrowser').checked) {
|
||||
document.getElementById('btnSave').removeAttribute('disabled');
|
||||
} else {
|
||||
document.getElementById('btnSave').setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
configWindow.on('loaded', function() {
|
||||
document.getElementById('status-text').innerHTML = '';
|
||||
// Get the config data from the file.
|
||||
let configData = misc.ConfigureStore.getConfigData();
|
||||
|
||||
// Set the GUI value as per configuration.
|
||||
if (configData['fixedPort']) {
|
||||
document.getElementById('fixedPortCheck').checked = true;
|
||||
document.getElementById('portNo').disabled = false;
|
||||
} else {
|
||||
document.getElementById('fixedPortCheck').checked = false;
|
||||
document.getElementById('portNo').disabled = true;
|
||||
}
|
||||
document.getElementById('portNo').value = configData['portNo'];
|
||||
document.getElementById('timeOut').value = configData['connectionTimeout'];
|
||||
|
||||
if (configData['openDocsInBrowser']) {
|
||||
document.getElementById('openDocsInBrowser').checked = true;
|
||||
} else {
|
||||
document.getElementById('openDocsInBrowser').checked = false;
|
||||
}
|
||||
// Add event listeners
|
||||
document.getElementById('btnSave').addEventListener('click', checkConfiguration);
|
||||
document.getElementById('fixedPortCheck').addEventListener('change', onCheckChange);
|
||||
document.getElementById('portNo').addEventListener('change', enableDisableSaveButton);
|
||||
document.getElementById('timeOut').addEventListener('change', enableDisableSaveButton);
|
||||
document.getElementById('openDocsInBrowser').addEventListener('change', enableDisableSaveButton);
|
||||
});
|
|
@ -0,0 +1,109 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { app, Menu, ipcMain } from 'electron';
|
||||
|
||||
const isMac = process.platform == 'darwin';
|
||||
let mainMenu;
|
||||
|
||||
function buildMenu(pgadminMenus, pgAdminMainScreen, callbacks) {
|
||||
const template = [];
|
||||
|
||||
// bind all menus click event.
|
||||
pgadminMenus = pgadminMenus.map((menuItem)=>{
|
||||
return {
|
||||
...menuItem,
|
||||
submenu: menuItem.submenu?.map((subMenuItem)=>{
|
||||
const smName = `${menuItem.name}_${subMenuItem.name}`;
|
||||
return {
|
||||
...subMenuItem,
|
||||
click: ()=>{
|
||||
pgAdminMainScreen.webContents.send('menu-click', smName);
|
||||
},
|
||||
submenu: subMenuItem.submenu?.map((deeperSubMenuItem)=>{
|
||||
return {
|
||||
...deeperSubMenuItem,
|
||||
click: ()=>{
|
||||
pgAdminMainScreen.webContents.send('menu-click', `${smName}_${deeperSubMenuItem.name}`);
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let menuFile = pgadminMenus.shift();
|
||||
template.push({
|
||||
...menuFile,
|
||||
submenu: [
|
||||
...menuFile.submenu,
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'View Logs...', click: callbacks['view_logs'],
|
||||
},
|
||||
{
|
||||
label: 'Configure runtime...', click: callbacks['configure'],
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ role: 'hide' },
|
||||
{ role: 'hideOthers' },
|
||||
{ role: 'unhide' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'quit' },
|
||||
],
|
||||
});
|
||||
|
||||
if(isMac) {
|
||||
template[0].label = app.name;
|
||||
}
|
||||
|
||||
// push all except help
|
||||
template.push(...pgadminMenus.slice(0, -1));
|
||||
|
||||
template.push(
|
||||
{ role: 'editMenu' },
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{ label: 'Reload', click: ()=>pgAdminMainScreen.webContents.reload()},
|
||||
{ label: 'Toggle Developer Tools', click: ()=>pgAdminMainScreen.webContents.openDevTools({ mode: 'bottom' })},
|
||||
{ type: 'separator' },
|
||||
{ role: 'resetZoom' },
|
||||
{ role: 'zoomIn' },
|
||||
{ role: 'zoomOut' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'togglefullscreen' },
|
||||
],
|
||||
},
|
||||
{ role: 'windowMenu' },
|
||||
);
|
||||
|
||||
template.push(pgadminMenus[pgadminMenus.length-1]);
|
||||
|
||||
return Menu.buildFromTemplate(template);
|
||||
}
|
||||
|
||||
export function setupMenu(pgAdminMainScreen, callbacks={}) {
|
||||
ipcMain.on('setMenus', (event, menus)=>{
|
||||
mainMenu = buildMenu(menus, pgAdminMainScreen, callbacks);
|
||||
if(isMac) {
|
||||
Menu.setApplicationMenu(mainMenu);
|
||||
} else {
|
||||
pgAdminMainScreen.setMenu(mainMenu);
|
||||
}
|
||||
|
||||
ipcMain.on('enable-disable-menu-items', (event, menu, menuItem)=>{
|
||||
const menuItemObj = mainMenu.getMenuItemById(menuItem?.id);
|
||||
if(menuItemObj) {
|
||||
menuItemObj.enabled = menuItem.isDisabled;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -7,16 +7,13 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const net = require('net');
|
||||
const {platform, homedir} = require('os');
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import net from 'net';
|
||||
import {platform} from 'os';
|
||||
import { app } from 'electron';
|
||||
|
||||
let pgadminServerProcess = null;
|
||||
let pgAdminWindowObject = null;
|
||||
let zoomInShortcut = null;
|
||||
let zoomOutShortcut = null;
|
||||
let actualSizeShortcut = null;
|
||||
let toggleFullScreenShortcut = null;
|
||||
|
||||
// This function is used to check whether directory is present or not
|
||||
// if not present then create it recursively
|
||||
|
@ -28,26 +25,31 @@ const createDir = (dirName) => {
|
|||
|
||||
const insideFlatpak = () => {
|
||||
return platform() === 'linux' && fs.existsSync('/.flatpak-info');
|
||||
}
|
||||
};
|
||||
|
||||
// This function is used to get the python executable path
|
||||
// based on the platform. Use this for deployment.
|
||||
const getPythonPath = () => {
|
||||
let pythonPath;
|
||||
export const getAppPaths = (basePath) => {
|
||||
let pythonPath, pgadminFile;
|
||||
switch (platform()) {
|
||||
case 'win32':
|
||||
pythonPath = '../python/python.exe';
|
||||
pythonPath = '../../../../../python/python.exe';
|
||||
pgadminFile = '../../../../../web/pgAdmin4.py';
|
||||
break;
|
||||
case 'darwin':
|
||||
pythonPath = '../../Frameworks/Python.framework/Versions/Current/bin/python3';
|
||||
pythonPath = '../../../../Frameworks/Python.framework/Versions/Current/bin/python3';
|
||||
pgadminFile = '../../../web/pgAdmin4.py';
|
||||
break;
|
||||
case 'linux':
|
||||
pythonPath = '../venv/bin/python3';
|
||||
pythonPath = '../../../../../venv/bin/python3';
|
||||
pgadminFile = '../../../../../web/pgAdmin4.py';
|
||||
if (insideFlatpak()) {
|
||||
pythonPath = '/usr/bin/python';
|
||||
pgadminFile = '/app/pgAdmin4/web/pgAdmin4.py';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pgadminFile = '../../../web/pgAdmin4.py';
|
||||
if (platform().startsWith('win')) {
|
||||
pythonPath = '../python/python.exe';
|
||||
} else {
|
||||
|
@ -55,69 +57,15 @@ const getPythonPath = () => {
|
|||
}
|
||||
}
|
||||
|
||||
return pythonPath;
|
||||
};
|
||||
|
||||
// This function is used to get the [roaming] app data path
|
||||
// based on the platform. Use this for config etc.
|
||||
const getAppDataPath = () => {
|
||||
let appDataPath;
|
||||
switch (platform()) {
|
||||
case 'win32':
|
||||
appDataPath = path.join(process.env.APPDATA, 'pgadmin');
|
||||
break;
|
||||
case 'darwin':
|
||||
appDataPath = path.join(homedir(), 'Library', 'Preferences', 'pgadmin');
|
||||
break;
|
||||
case 'linux':
|
||||
if ('XDG_CONFIG_HOME' in process.env) {
|
||||
appDataPath = path.join(process.env.XDG_CONFIG_HOME, 'pgadmin');
|
||||
} else {
|
||||
appDataPath = path.join(homedir(), '.config', 'pgadmin');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (platform().startsWith('win')) {
|
||||
appDataPath = path.join(process.env.APPDATA, 'pgadmin');
|
||||
} else if ('XDG_CONFIG_HOME' in process.env) {
|
||||
appDataPath = path.join(process.env.XDG_CONFIG_HOME, 'pgadmin');
|
||||
} else {
|
||||
appDataPath = path.join(homedir(), '.config', 'pgadmin');
|
||||
}
|
||||
}
|
||||
|
||||
// Create directory if not exists
|
||||
createDir(appDataPath);
|
||||
|
||||
return appDataPath;
|
||||
return [path.join(basePath, pythonPath), path.join(basePath, pgadminFile)];
|
||||
};
|
||||
|
||||
// This function is used to get the [local] app data path
|
||||
// based on the platform. Use this for logs etc.
|
||||
const getLocalAppDataPath = () => {
|
||||
let localAppDataPath;
|
||||
switch (platform()) {
|
||||
case 'win32':
|
||||
localAppDataPath = path.join(process.env.LOCALAPPDATA, 'pgadmin');
|
||||
break;
|
||||
case 'darwin':
|
||||
localAppDataPath = path.join(homedir(), 'Library', 'Application Support', 'pgadmin');
|
||||
break;
|
||||
case 'linux':
|
||||
if ('XDG_DATA_HOME' in process.env) {
|
||||
localAppDataPath = path.join(process.env.XDG_DATA_HOME, 'pgadmin');
|
||||
} else {
|
||||
localAppDataPath = path.join(homedir(), '.local', 'share', 'pgadmin');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (platform().startsWith('win')) {
|
||||
localAppDataPath = path.join(process.env.LOCALAPPDATA, 'pgadmin');
|
||||
} else if ('XDG_DATA_HOME' in process.env) {
|
||||
localAppDataPath = path.join(process.env.XDG_DATA_HOME, 'pgadmin');
|
||||
} else {
|
||||
localAppDataPath = path.join(homedir(), '.local', 'share', 'pgadmin');
|
||||
}
|
||||
let localAppDataPath = app.getPath('userData');
|
||||
if(process.platform == 'linux' && 'XDG_DATA_HOME' in process.env) {
|
||||
localAppDataPath = path.join(process.env.XDG_DATA_HOME, app.name);
|
||||
}
|
||||
|
||||
// Create directory if not exists
|
||||
|
@ -128,14 +76,14 @@ const getLocalAppDataPath = () => {
|
|||
|
||||
// This function is used to get the random available TCP port
|
||||
// if fixedPort is set to 0. Else check whether port is in used or not.
|
||||
const getAvailablePort = (fixedPort) => {
|
||||
export const getAvailablePort = (fixedPort) => {
|
||||
return new Promise(function(resolve, reject) {
|
||||
const server = net.createServer();
|
||||
|
||||
server.listen(fixedPort, '127.0.0.1');
|
||||
|
||||
server.on('error', (e) => {
|
||||
reject(e.code);
|
||||
reject(e instanceof Error ? e : new Error(e.code));
|
||||
});
|
||||
|
||||
server.on('listening', () => {
|
||||
|
@ -147,13 +95,10 @@ const getAvailablePort = (fixedPort) => {
|
|||
};
|
||||
|
||||
// Get the app data folder path
|
||||
const currentTime = (new Date()).getTime();
|
||||
const serverLogFile = path.join(getLocalAppDataPath(), 'pgadmin4.' + currentTime.toString() + '.log');
|
||||
const configFileName = path.join(getAppDataPath(), 'runtime_config.json');
|
||||
const DEFAULT_CONFIG_DATA = {'fixedPort': false, 'portNo': 5050, 'connectionTimeout': 90, 'zoomLevel': 0, 'openDocsInBrowser': true};
|
||||
const serverLogFile = path.join(getLocalAppDataPath(), 'pgadmin4.' + (new Date()).getTime().toString() + '.log');
|
||||
|
||||
// This function is used to read the file and return the content
|
||||
const readServerLog = () => {
|
||||
export const readServerLog = () => {
|
||||
let data = null;
|
||||
|
||||
if (fs.existsSync(serverLogFile)) {
|
||||
|
@ -168,7 +113,7 @@ const readServerLog = () => {
|
|||
};
|
||||
|
||||
// This function is used to write the data into the log file
|
||||
const writeServerLog = (data) => {
|
||||
export const writeServerLog = (data) => {
|
||||
data = data + '\n';
|
||||
if (fs.existsSync(serverLogFile)) {
|
||||
fs.writeFileSync(serverLogFile, data, {flag: 'a+'});
|
||||
|
@ -185,28 +130,18 @@ const removeLogFile = () => {
|
|||
};
|
||||
|
||||
// This function used to set the object of pgAdmin server process.
|
||||
const setProcessObject = (processObject) => {
|
||||
export const setProcessObject = (processObject) => {
|
||||
pgadminServerProcess = processObject;
|
||||
};
|
||||
|
||||
// This function used to set the object of pgAdmin window.
|
||||
const setPgAdminWindowObject = (windowObject) => {
|
||||
pgAdminWindowObject = windowObject;
|
||||
};
|
||||
|
||||
// This function is used to get the server log file.
|
||||
const getServerLogFile = () => {
|
||||
export const getServerLogFile = () => {
|
||||
return serverLogFile;
|
||||
};
|
||||
|
||||
// This function is used to get the runtime config file.
|
||||
const getRunTimeConfigFile = () => {
|
||||
return configFileName;
|
||||
};
|
||||
|
||||
// This function is used to kill the server process, remove the log files
|
||||
// and quit the application.
|
||||
const cleanupAndQuitApp = () => {
|
||||
export const cleanupAndQuitApp = (pgAdminWindowObject) => {
|
||||
// Remove the server log file on exit
|
||||
removeLogFile();
|
||||
|
||||
|
@ -216,247 +151,14 @@ const cleanupAndQuitApp = () => {
|
|||
process.kill(pgadminServerProcess.pid);
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Failed to kill server process.');
|
||||
console.warn('Failed to kill server process.', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (pgAdminWindowObject != null) {
|
||||
// Remove all the cookies.
|
||||
pgAdminWindowObject.cookies.getAll({}, function(cookies) {
|
||||
try {
|
||||
cookies.forEach(function(cookie) {
|
||||
pgAdminWindowObject.cookies.remove({url: 'http://' + cookie.domain, name: cookie.name});
|
||||
const ses = pgAdminWindowObject.webContents.session;
|
||||
ses.clearStorageData({
|
||||
storages: ['cookies'],
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('Failed to remove cookies.');
|
||||
} finally {
|
||||
pgAdminWindowObject = null;
|
||||
// Quit Application
|
||||
nw.App.quit();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Quit Application
|
||||
nw.App.quit();
|
||||
}
|
||||
};
|
||||
|
||||
// This function is used to create zoom events based on platform
|
||||
const setZoomEvents = () => {
|
||||
if (platform() == 'darwin') {
|
||||
zoomInShortcut = new nw.Shortcut({key: 'Command+Equal'});
|
||||
zoomOutShortcut = new nw.Shortcut({key: 'Command+Minus'});
|
||||
actualSizeShortcut = new nw.Shortcut({key: 'Command+0'});
|
||||
toggleFullScreenShortcut = new nw.Shortcut({key: 'Command+Ctrl+F'});
|
||||
} else {
|
||||
zoomInShortcut = new nw.Shortcut({key: 'Ctrl+Equal'});
|
||||
zoomOutShortcut = new nw.Shortcut({key: 'Ctrl+Minus'});
|
||||
actualSizeShortcut = new nw.Shortcut({key: 'Ctrl+0'});
|
||||
// Use F10 instead of F11. F11 does not work possibly due to Chromium reserving it for their function.
|
||||
toggleFullScreenShortcut = new nw.Shortcut({key: 'F10'});
|
||||
}
|
||||
|
||||
zoomInShortcut.on('active', function() {
|
||||
zoomIn();
|
||||
});
|
||||
|
||||
zoomOutShortcut.on('active', function() {
|
||||
zoomOut();
|
||||
});
|
||||
|
||||
actualSizeShortcut.on('active', function() {
|
||||
actualSize();
|
||||
});
|
||||
|
||||
toggleFullScreenShortcut.on('active', function() {
|
||||
toggleFullScreen();
|
||||
});
|
||||
|
||||
zoomInShortcut.on('failed', function(msg) {
|
||||
let errMsg = 'Failed to register zoom in shortcut with error: ' + msg;
|
||||
console.warn(errMsg);
|
||||
});
|
||||
|
||||
zoomOutShortcut.on('failed', function(msg) {
|
||||
let errMsg = 'Failed to register zoom out shortcut with error: ' + msg;
|
||||
console.warn(errMsg);
|
||||
});
|
||||
|
||||
actualSizeShortcut.on('failed', function(msg) {
|
||||
let errMsg = 'Failed to register actual size shortcut with error: ' + msg;
|
||||
console.warn(errMsg);
|
||||
});
|
||||
|
||||
toggleFullScreenShortcut.on('failed', function(msg) {
|
||||
let errMsg = 'Failed to register toggle full screen shortcut with error: ' + msg;
|
||||
console.warn(errMsg);
|
||||
});
|
||||
};
|
||||
|
||||
// This function is used to iterate all open windows and set the zoom level.
|
||||
const setZoomLevelForAllWindows = () => {
|
||||
nw.Window.getAll(function(winArray) {
|
||||
for (let arr_val of winArray) {
|
||||
arr_val.zoomLevel = pgAdminWindowObject.zoomLevel;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// This function used to zoom in the pgAdmin window.
|
||||
const zoomIn = () => {
|
||||
if (pgAdminWindowObject != null) {
|
||||
pgAdminWindowObject.zoomLevel += 0.5;
|
||||
setZoomLevelForAllWindows();
|
||||
ConfigureStore.set('zoomLevel', pgAdminWindowObject.zoomLevel);
|
||||
ConfigureStore.saveConfig();
|
||||
}
|
||||
};
|
||||
|
||||
// This function used to zoom out the pgAdmin window.
|
||||
const zoomOut = () => {
|
||||
if (pgAdminWindowObject != null) {
|
||||
pgAdminWindowObject.zoomLevel -= 0.5;
|
||||
setZoomLevelForAllWindows();
|
||||
ConfigureStore.set('zoomLevel', pgAdminWindowObject.zoomLevel);
|
||||
ConfigureStore.saveConfig();
|
||||
}
|
||||
};
|
||||
|
||||
// This function used to reset the zoom level of pgAdmin window.
|
||||
const actualSize = () => {
|
||||
if (pgAdminWindowObject != null) {
|
||||
pgAdminWindowObject.zoomLevel = 0;
|
||||
setZoomLevelForAllWindows();
|
||||
ConfigureStore.set('zoomLevel', pgAdminWindowObject.zoomLevel);
|
||||
ConfigureStore.saveConfig();
|
||||
}
|
||||
};
|
||||
|
||||
const toggleFullScreen = () => {
|
||||
if (pgAdminWindowObject != null) {
|
||||
// Toggle full screen
|
||||
pgAdminWindowObject.toggleFullscreen();
|
||||
|
||||
// Change the menu label.
|
||||
let menu_label = pgAdminWindowObject.window.document.querySelector('#mnu_toggle_fullscreen_runtime span').innerHTML;
|
||||
if (menu_label.indexOf('Enter Full Screen') >= 0) {
|
||||
pgAdminWindowObject.window.document.querySelector('#mnu_toggle_fullscreen_runtime span').innerHTML = menu_label.replace('Enter', 'Exit');
|
||||
} else if (menu_label.indexOf('Exit Full Screen') >= 0) {
|
||||
pgAdminWindowObject.window.document.querySelector('#mnu_toggle_fullscreen_runtime span').innerHTML = menu_label.replace('Exit', 'Enter');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This function is used to register zoom events.
|
||||
const registerZoomEvents = () => {
|
||||
nw.App.registerGlobalHotKey(zoomInShortcut);
|
||||
nw.App.registerGlobalHotKey(zoomOutShortcut);
|
||||
nw.App.registerGlobalHotKey(actualSizeShortcut);
|
||||
nw.App.registerGlobalHotKey(toggleFullScreenShortcut);
|
||||
};
|
||||
|
||||
// This function is used to unregister zoom events.
|
||||
const unregisterZoomEvents = () => {
|
||||
nw.App.unregisterGlobalHotKey(zoomInShortcut);
|
||||
nw.App.unregisterGlobalHotKey(zoomOutShortcut);
|
||||
nw.App.unregisterGlobalHotKey(actualSizeShortcut);
|
||||
nw.App.unregisterGlobalHotKey(toggleFullScreenShortcut);
|
||||
};
|
||||
|
||||
let ConfigureStore = {
|
||||
fileName: configFileName,
|
||||
jsonData: {},
|
||||
|
||||
init: function() {
|
||||
if (!this.readConfig()){
|
||||
this.jsonData = DEFAULT_CONFIG_DATA;
|
||||
this.saveConfig();
|
||||
}
|
||||
},
|
||||
|
||||
// This function is used to write configuration data
|
||||
saveConfig: function() {
|
||||
fs.writeFileSync(this.fileName, JSON.stringify(this.jsonData, null, 4), {flag: 'w'});
|
||||
},
|
||||
|
||||
// This function is used to read the configuration data
|
||||
readConfig: function() {
|
||||
if (fs.existsSync(this.fileName)) {
|
||||
try {
|
||||
this.jsonData = JSON.parse(fs.readFileSync(this.fileName));
|
||||
} catch (error) {
|
||||
/* If the file is not present or invalid JSON data in file */
|
||||
this.jsonData = {};
|
||||
}
|
||||
} else {
|
||||
let errMsg = 'Unable to read file ' + this.fileName + ' not found.';
|
||||
console.warn(errMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
getConfigData: function() {
|
||||
return this.jsonData;
|
||||
},
|
||||
|
||||
get: function(key, if_not_value) {
|
||||
if(this.jsonData[key] !== undefined) {
|
||||
return this.jsonData[key];
|
||||
} else {
|
||||
return if_not_value;
|
||||
}
|
||||
},
|
||||
|
||||
set: function(key, value) {
|
||||
if(typeof key === 'object'){
|
||||
this.jsonData = {
|
||||
...this.jsonData,
|
||||
...key,
|
||||
};
|
||||
} else if(value === '' || value == null || typeof(value) == 'undefined') {
|
||||
if(this.jsonData[key] !== undefined) {
|
||||
delete this.jsonData[key];
|
||||
}
|
||||
} else {
|
||||
this.jsonData[key] = value;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function parseConsoleArgs(_method, args) {
|
||||
const retData = Array.from(args).map(arg => {
|
||||
try {
|
||||
if(arg.stack) return arg.stack;
|
||||
return JSON.stringify(arg);
|
||||
} catch (e) {
|
||||
return arg
|
||||
}
|
||||
});
|
||||
return retData?.join(' ');
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
readServerLog: readServerLog,
|
||||
writeServerLog: writeServerLog,
|
||||
getAvailablePort: getAvailablePort,
|
||||
getPythonPath: getPythonPath,
|
||||
setProcessObject: setProcessObject,
|
||||
cleanupAndQuitApp: cleanupAndQuitApp,
|
||||
getServerLogFile: getServerLogFile,
|
||||
getRunTimeConfigFile: getRunTimeConfigFile,
|
||||
setPgAdminWindowObject: setPgAdminWindowObject,
|
||||
zoomIn: zoomIn,
|
||||
zoomOut: zoomOut,
|
||||
actualSize: actualSize,
|
||||
toggleFullScreen: toggleFullScreen,
|
||||
setZoomEvents: setZoomEvents,
|
||||
registerZoomEvents: registerZoomEvents,
|
||||
unregisterZoomEvents: unregisterZoomEvents,
|
||||
setZoomLevelForAllWindows: setZoomLevelForAllWindows,
|
||||
ConfigureStore: ConfigureStore,
|
||||
parseConsoleArgs: parseConsoleArgs,
|
||||
insideFlatpak: insideFlatpak,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
const { contextBridge, ipcRenderer } = require('electron/renderer');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronUI', {
|
||||
getConfigData: (key) => ipcRenderer.invoke('getStoreData', key),
|
||||
setConfigData: (newValues) => ipcRenderer.invoke('setStoreData', newValues),
|
||||
showMessageBox: (options) => ipcRenderer.invoke('showMessageBox', options),
|
||||
restartApp: ()=>ipcRenderer.send('restartApp'),
|
||||
getServerLogFile: ()=>ipcRenderer.invoke('getServerLogFile'),
|
||||
readServerLog: ()=>ipcRenderer.invoke('readServerLog'),
|
||||
checkPortAvailable: (port)=>ipcRenderer.invoke('checkPortAvailable', port),
|
||||
openConfigure: ()=>ipcRenderer.invoke('openConfigure'),
|
||||
});
|
|
@ -6,44 +6,94 @@
|
|||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const misc = require('../js/misc.js');
|
||||
const spawn = require('child_process').spawn;
|
||||
const {EOL} = require('os');
|
||||
import { app, BrowserWindow, dialog, ipcMain, Menu, shell } from 'electron';
|
||||
import axios from 'axios';
|
||||
import Store from 'electron-store';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import * as misc from './misc.js';
|
||||
import { spawn } from 'child_process';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { setupMenu } from './menu.js';
|
||||
|
||||
const configStore = new Store({
|
||||
defaults: {
|
||||
fixedPort: false,
|
||||
portNo: 5050,
|
||||
connectionTimeout: 180,
|
||||
openDocsInBrowser: true,
|
||||
},
|
||||
});
|
||||
let pgadminServerProcess = null;
|
||||
let startPageUrl = null;
|
||||
let serverCheckUrl = null;
|
||||
let addMenuCompleted = false;
|
||||
let pgAdminMainScreen = null;
|
||||
|
||||
let serverPort = 5050;
|
||||
let appStartTime = (new Date()).getTime();
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
let docsURLSubStrings = ['www.enterprisedb.com', 'www.postgresql.org', 'www.pgadmin.org', 'help/help'];
|
||||
|
||||
// Paths to the rest of the app
|
||||
let pythonPath = misc.getPythonPath();
|
||||
let pgadminFile = '../web/pgAdmin4.py';
|
||||
let configFile = '../web/config.py';
|
||||
process.env['ELECTRON_ENABLE_SECURITY_WARNINGS'] = false;
|
||||
|
||||
if (misc.insideFlatpak()) {
|
||||
pgadminFile = '/app/pgAdmin4/web/pgAdmin4.py';
|
||||
}
|
||||
// Paths to the rest of the app
|
||||
|
||||
let [pythonPath, pgadminFile] = misc.getAppPaths(__dirname);
|
||||
|
||||
// Override the paths above, if a developer needs to
|
||||
if (fs.existsSync('dev_config.json')) {
|
||||
try {
|
||||
let dev_config = JSON.parse(fs.readFileSync('dev_config.json'));
|
||||
pythonPath = dev_config['pythonPath'];
|
||||
pgadminFile = dev_config['pgadminFile'];
|
||||
pythonPath = path.resolve(dev_config['pythonPath']);
|
||||
pgadminFile = path.resolve(dev_config['pgadminFile']);
|
||||
} catch (error) {
|
||||
// Meh.
|
||||
console.error('Failed to load dev_config', error);
|
||||
}
|
||||
}
|
||||
|
||||
Menu.setApplicationMenu(null);
|
||||
|
||||
function openConfigure() {
|
||||
const win = new BrowserWindow({
|
||||
show: false,
|
||||
width: 600,
|
||||
height: 600,
|
||||
position: 'center',
|
||||
resizable: false,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'other_preload.js'),
|
||||
},
|
||||
});
|
||||
win.loadFile('./src/html/configure.html');
|
||||
win.once('ready-to-show', ()=>{
|
||||
win.show();
|
||||
});
|
||||
}
|
||||
|
||||
function showErrorDialog(intervalID) {
|
||||
if(!splashWindow.isVisible()) {
|
||||
return;
|
||||
}
|
||||
clearInterval(intervalID);
|
||||
splashWindow.close();
|
||||
|
||||
new BrowserWindow({
|
||||
'frame': true,
|
||||
'width': 790,
|
||||
'height': 430,
|
||||
'position': 'center',
|
||||
'resizable': false,
|
||||
'focus': true,
|
||||
'show': true,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'other_preload.js'),
|
||||
},
|
||||
}).loadFile('./src/html/server_error.html');
|
||||
}
|
||||
|
||||
// This functions is used to start the pgAdmin4 server by spawning a
|
||||
// separate process.
|
||||
function startDesktopMode() {
|
||||
|
@ -52,6 +102,7 @@ function startDesktopMode() {
|
|||
if (pgadminServerProcess != null)
|
||||
return;
|
||||
|
||||
let pingIntervalID;
|
||||
let UUID = crypto.randomUUID();
|
||||
// Set the environment variables so that pgAdmin 4 server
|
||||
// starts listening on the appropriate port.
|
||||
|
@ -63,28 +114,25 @@ function startDesktopMode() {
|
|||
startPageUrl = 'http://127.0.0.1:' + serverPort + '/?key=' + UUID;
|
||||
serverCheckUrl = 'http://127.0.0.1:' + serverPort + '/misc/ping?key=' + UUID;
|
||||
|
||||
document.getElementById('loader-text-status').innerHTML = 'Starting pgAdmin 4...';
|
||||
|
||||
// Write Python Path, pgAdmin file path and command in log file.
|
||||
misc.writeServerLog('pgAdmin Runtime Environment');
|
||||
misc.writeServerLog('--------------------------------------------------------');
|
||||
let command = path.resolve(pythonPath) + ' -s ' + path.resolve(pgadminFile);
|
||||
misc.writeServerLog('Python Path: "' + path.resolve(pythonPath) + '"');
|
||||
misc.writeServerLog('Runtime Config File: "' + path.resolve(misc.getRunTimeConfigFile()) + '"');
|
||||
misc.writeServerLog('pgAdmin Config File: "' + path.resolve(configFile) + '"');
|
||||
misc.writeServerLog('Webapp Path: "' + path.resolve(pgadminFile) + '"');
|
||||
let command = pythonPath + ' -s ' + pgadminFile;
|
||||
misc.writeServerLog('Python Path: "' + pythonPath + '"');
|
||||
misc.writeServerLog('Runtime Config File: "' + path.resolve(configStore.path) + '"');
|
||||
misc.writeServerLog('Webapp Path: "' + pgadminFile + '"');
|
||||
misc.writeServerLog('pgAdmin Command: "' + command + '"');
|
||||
misc.writeServerLog('Environment: ');
|
||||
Object.keys(process.env).forEach(function (key) {
|
||||
// Below code is included only for Mac OS as default path for azure CLI
|
||||
// installation path is not included in PATH variable while spawning
|
||||
// runtime environment.
|
||||
if (platform() === 'darwin' && key === 'PATH') {
|
||||
if (process.platform === 'darwin' && key === 'PATH') {
|
||||
let updated_path = process.env[key] + ':/usr/local/bin';
|
||||
process.env[key] = updated_path;
|
||||
}
|
||||
|
||||
if (platform() === 'win32' && key.toUpperCase() === 'PATH') {
|
||||
if (process.platform === 'win32' && key.toUpperCase() === 'PATH') {
|
||||
let _libpq_path = path.join(path.dirname(path.dirname(path.resolve(pgadminFile))), 'runtime');
|
||||
process.env[key] = _libpq_path + ';' + process.env[key];
|
||||
}
|
||||
|
@ -95,12 +143,14 @@ function startDesktopMode() {
|
|||
|
||||
// Spawn the process to start pgAdmin4 server.
|
||||
let spawnStartTime = (new Date).getTime();
|
||||
pgadminServerProcess = spawn(path.resolve(pythonPath), ['-s', path.resolve(pgadminFile)]);
|
||||
pgadminServerProcess = spawn(pythonPath, ['-s', pgadminFile]);
|
||||
pgadminServerProcess.on('error', function (err) {
|
||||
// Log the error into the log file if process failed to launch
|
||||
misc.writeServerLog('Failed to launch pgAdmin4. Error:');
|
||||
misc.writeServerLog(err);
|
||||
showErrorDialog(pingIntervalID);
|
||||
});
|
||||
|
||||
let spawnEndTime = (new Date).getTime();
|
||||
misc.writeServerLog('Total spawn time to start the pgAdmin4 server: ' + (spawnEndTime - spawnStartTime) / 1000 + ' Sec');
|
||||
|
||||
|
@ -120,7 +170,7 @@ function startDesktopMode() {
|
|||
return axios.get(serverCheckUrl);
|
||||
}
|
||||
|
||||
let connectionTimeout = misc.ConfigureStore.get('connectionTimeout', 90) * 1000;
|
||||
let connectionTimeout = configStore.get('connectionTimeout', 180) * 1000;
|
||||
let currentTime = (new Date).getTime();
|
||||
let endTime = currentTime + connectionTimeout;
|
||||
let midTime1 = currentTime + (connectionTimeout / 2);
|
||||
|
@ -129,7 +179,7 @@ function startDesktopMode() {
|
|||
|
||||
// ping pgAdmin server every 1 second.
|
||||
let pingStartTime = (new Date).getTime();
|
||||
let intervalID = setInterval(function () {
|
||||
pingIntervalID = setInterval(function () {
|
||||
// If ping request is already send and response is not
|
||||
// received no need to send another request.
|
||||
if (pingInProgress)
|
||||
|
@ -137,11 +187,11 @@ function startDesktopMode() {
|
|||
|
||||
pingServer().then(() => {
|
||||
pingInProgress = false;
|
||||
document.getElementById('loader-text-status').innerHTML = 'pgAdmin 4 started';
|
||||
splashWindow.webContents.executeJavaScript('document.getElementById(\'loader-text-status\').innerHTML = \'pgAdmin 4 started\';', true);
|
||||
// Set the pgAdmin process object to misc
|
||||
misc.setProcessObject(pgadminServerProcess);
|
||||
|
||||
clearInterval(intervalID);
|
||||
clearInterval(pingIntervalID);
|
||||
let appEndTime = (new Date).getTime();
|
||||
misc.writeServerLog('------------------------------------------');
|
||||
misc.writeServerLog('Total time taken to ping pgAdmin4 server: ' + (appEndTime - pingStartTime) / 1000 + ' Sec');
|
||||
|
@ -155,33 +205,20 @@ function startDesktopMode() {
|
|||
// if the connection timeout has lapsed then throw an error
|
||||
// and stop pinging the server.
|
||||
if (curTime >= endTime) {
|
||||
clearInterval(intervalID);
|
||||
splashWindow.hide();
|
||||
|
||||
nw.Window.open('src/html/server_error.html', {
|
||||
'frame': true,
|
||||
'width': 790,
|
||||
'height': 430,
|
||||
'position': 'center',
|
||||
'resizable': false,
|
||||
'focus': true,
|
||||
'show': true,
|
||||
});
|
||||
showErrorDialog(pingIntervalID);
|
||||
}
|
||||
|
||||
if (curTime > midTime1) {
|
||||
if (curTime < midTime2) {
|
||||
document.getElementById('loader-text-status').innerHTML = 'Taking longer than usual...';
|
||||
splashWindow.webContents.executeJavaScript('document.getElementById(\'loader-text-status\').innerHTML = \'Taking longer than usual...\';', true);
|
||||
} else {
|
||||
document.getElementById('loader-text-status').innerHTML = 'Almost there...';
|
||||
splashWindow.webContents.executeJavaScript('document.getElementById(\'loader-text-status\').innerHTML = \'Almost there...\';', true);
|
||||
}
|
||||
} else {
|
||||
document.getElementById('loader-text-status').innerHTML = 'Waiting for pgAdmin 4 to start...';
|
||||
}
|
||||
});
|
||||
|
||||
pingInProgress = true;
|
||||
}, 250);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// This function is used to hide the splash screen and create/launch
|
||||
|
@ -189,7 +226,7 @@ function startDesktopMode() {
|
|||
function launchPgAdminWindow() {
|
||||
// Create and launch new window and open pgAdmin url
|
||||
misc.writeServerLog('Application Server URL: ' + startPageUrl);
|
||||
nw.Window.open(startPageUrl, {
|
||||
pgAdminMainScreen = new BrowserWindow({
|
||||
'id': 'pgadmin-main',
|
||||
'icon': '../../assets/pgAdmin4.png',
|
||||
'frame': true,
|
||||
|
@ -201,158 +238,130 @@ function launchPgAdminWindow() {
|
|||
'height': 768,
|
||||
'focus': true,
|
||||
'show': false,
|
||||
}, (pgadminWindow) => {
|
||||
pgAdminMainScreen = pgadminWindow;
|
||||
// Set pgAdmin4 Windows Object
|
||||
misc.setPgAdminWindowObject(pgadminWindow);
|
||||
|
||||
// Set the zoom level stored in the config file.
|
||||
pgadminWindow.zoomLevel = misc.ConfigureStore.get('zoomLevel', 0);
|
||||
|
||||
// Set zoom in and out events.
|
||||
misc.setZoomEvents();
|
||||
|
||||
// Workaround to fix increasing window size.
|
||||
// https://github.com/nwjs/nw.js/issues/7973
|
||||
pgadminWindow.on('close', function () {
|
||||
// Resize Window
|
||||
let resizeHeightBy = pgadminWindow.window.outerHeight - pgadminWindow.window.innerHeight;
|
||||
pgadminWindow.resizeBy(0, -resizeHeightBy);
|
||||
// Remove 'close' event handler, and then close window
|
||||
pgadminWindow.removeAllListeners('close');
|
||||
pgadminWindow.close()
|
||||
webPreferences: {
|
||||
nodeIntegrationInSubFrames: true,
|
||||
preload: path.join(__dirname, 'pgadmin_preload.js'),
|
||||
},
|
||||
});
|
||||
|
||||
pgadminWindow.on('closed', function () {
|
||||
misc.cleanupAndQuitApp();
|
||||
splashWindow.close();
|
||||
pgAdminMainScreen.webContents.session.clearCache();
|
||||
|
||||
setupMenu(pgAdminMainScreen, {
|
||||
'view_logs': ()=>{
|
||||
const win = new BrowserWindow({
|
||||
show: false,
|
||||
width: 790,
|
||||
height: 425,
|
||||
position: 'center',
|
||||
resizable: false,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'other_preload.js'),
|
||||
},
|
||||
});
|
||||
win.loadFile('./src/html/view_log.html');
|
||||
win.once('ready-to-show', ()=>{
|
||||
win.show();
|
||||
});
|
||||
},
|
||||
'configure': openConfigure,
|
||||
});
|
||||
|
||||
// set up handler for new-win-policy event.
|
||||
// Set the width and height for the new window.
|
||||
pgadminWindow.on('new-win-policy', function (frame, url, policy) {
|
||||
if (!frame) {
|
||||
let openDocsInBrowser = misc.ConfigureStore.get('openDocsInBrowser', true);
|
||||
pgAdminMainScreen.loadURL(startPageUrl);
|
||||
pgAdminMainScreen.setBounds(configStore.get('bounds'));
|
||||
pgAdminMainScreen.show();
|
||||
|
||||
pgAdminMainScreen.webContents.setWindowOpenHandler(({url})=>{
|
||||
let openDocsInBrowser = configStore.get('openDocsInBrowser', true);
|
||||
let isDocURL = false;
|
||||
docsURLSubStrings.forEach(function (key) {
|
||||
if (url.indexOf(key) >= 0) {
|
||||
isDocURL = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
if (openDocsInBrowser && isDocURL) {
|
||||
// Do not open the window
|
||||
policy.ignore();
|
||||
// Open URL in the external browser.
|
||||
nw.Shell.openExternal(url);
|
||||
shell.openExternal(url);
|
||||
return { action: 'deny' };
|
||||
} else {
|
||||
policy.setNewWindowManifest({
|
||||
'icon': '../../assets/pgAdmin4.png',
|
||||
'frame': true,
|
||||
return {
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: {
|
||||
'position': 'center',
|
||||
'min_width': 640,
|
||||
'min_height': 480,
|
||||
'width': pgadminWindow.width,
|
||||
'height': pgadminWindow.height,
|
||||
});
|
||||
}
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
...pgAdminMainScreen.getBounds(),
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'pgadmin_preload.js'),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
pgadminWindow.on('loaded', function () {
|
||||
/* Make the new window opener to null as it is
|
||||
* nothing but a splash screen. We will have to make it null,
|
||||
* so that open in new browser tab will work.
|
||||
*/
|
||||
pgadminWindow.window.hookConsole((method, args)=>{
|
||||
misc.writeServerLog(
|
||||
`--------------[UI ${method}]---------------${EOL}${misc.parseConsoleArgs(method, args)}${EOL}------------[UI End]----------------`);
|
||||
pgAdminMainScreen.on('close', () => {
|
||||
configStore.set('bounds', pgAdminMainScreen.getBounds());
|
||||
misc.cleanupAndQuitApp(pgAdminMainScreen);
|
||||
pgAdminMainScreen.removeAllListeners('close');
|
||||
pgAdminMainScreen.close();
|
||||
});
|
||||
pgadminWindow.window.opener = null;
|
||||
|
||||
// Show new window
|
||||
pgadminWindow.show();
|
||||
pgadminWindow.focus();
|
||||
|
||||
nativeMenu = new gui.Menu({ type: 'menubar' });
|
||||
// Create Mac Builtin Menu
|
||||
if (platform() === 'darwin') {
|
||||
nativeMenu.createMacBuiltin('pgAdmin 4');
|
||||
// Remove 'About pgAdmin 4' submenu
|
||||
nativeMenu?.items[0].submenu.removeAt(0);
|
||||
// Remove 'Close Window' submenu
|
||||
nativeMenu?.items[2].submenu.removeAt(1);
|
||||
pgAdminMainScreen.menu = nativeMenu;
|
||||
}
|
||||
|
||||
let splashWindow;
|
||||
|
||||
// setup preload events.
|
||||
ipcMain.handle('showOpenDialog', (_e, options) => dialog.showOpenDialog(options));
|
||||
ipcMain.handle('showSaveDialog', (_e, options) => dialog.showSaveDialog(options));
|
||||
ipcMain.handle('showMessageBox', (_e, options) => dialog.showMessageBox(options));
|
||||
ipcMain.handle('getStoreData', (_e, key) => key ? configStore.get(key) : configStore.store);
|
||||
ipcMain.handle('setStoreData', (_e, newValues) => {
|
||||
configStore.store = {
|
||||
...configStore.store,
|
||||
...newValues,
|
||||
};
|
||||
});
|
||||
ipcMain.handle('getServerLogFile', () => misc.getServerLogFile());
|
||||
ipcMain.handle('readServerLog', () => misc.readServerLog());
|
||||
ipcMain.handle('restartApp', ()=>{
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
});
|
||||
ipcMain.handle('log', (text) => ()=>{
|
||||
misc.writeServerLog(text);
|
||||
});
|
||||
ipcMain.handle('checkPortAvailable', async (_e, fixedPort)=>{
|
||||
try {
|
||||
pgAdminMainScreen.isCustomMenusAdded = false;
|
||||
let addMenuInterval = setInterval(() => {
|
||||
if (pgadminWindow?.window?.pgAdmin?.Browser?.Events && pgadminWindow?.window?.pgAdmin?.Browser?.MainMenus?.length > 0) {
|
||||
pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-enable-disable-menu-items', enableDisableMenuItem);
|
||||
pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-refresh-menu-item', refreshMenuItems);
|
||||
pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-update-checked-menu-item', updateCheckedMenuItem);
|
||||
pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-set-new-window-open-size', setNewWindowSize)
|
||||
// Add Main Menus to native menu.
|
||||
pgadminWindow.window.pgAdmin.Browser.MainMenus.forEach((menu)=> {
|
||||
addMenu(menu)
|
||||
})
|
||||
clearInterval(addMenuInterval);
|
||||
}
|
||||
}, 250)
|
||||
} catch (e) {
|
||||
console.error('Error in add native menus');
|
||||
await misc.getAvailablePort(fixedPort);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
ipcMain.handle('openConfigure', openConfigure);
|
||||
|
||||
// Hide the splash screen
|
||||
splashWindow.hide();
|
||||
app.whenReady().then(() => {
|
||||
splashWindow = new BrowserWindow({
|
||||
transparent: true,
|
||||
width: 750,
|
||||
height: 600,
|
||||
frame: false,
|
||||
movable: true,
|
||||
focusable: true,
|
||||
resizable: false,
|
||||
show: false,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
});
|
||||
|
||||
pgadminWindow.on('blur', function () {
|
||||
misc.unregisterZoomEvents();
|
||||
});
|
||||
splashWindow.loadFile('./src/html/splash.html');
|
||||
splashWindow.center();
|
||||
|
||||
pgadminWindow.on('focus', function () {
|
||||
misc.registerZoomEvents();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Get the gui object of NW.js
|
||||
let gui = require('nw.gui');
|
||||
let splashWindow = gui.Window.get();
|
||||
|
||||
// Enable dragging on the splash screen.
|
||||
let isDragging = false;
|
||||
let dragOrigin = { x: 0, y: 0 };
|
||||
document.mouseleave = () => isDragging = false;
|
||||
document.onmouseup = () => isDragging = false;
|
||||
|
||||
document.onmousedown = (e) => {
|
||||
isDragging = true;
|
||||
dragOrigin.x = e.x;
|
||||
dragOrigin.y = e.y;
|
||||
};
|
||||
|
||||
document.onmousemove = (e) => {
|
||||
if (isDragging) {
|
||||
splashWindow.moveTo(e.screenX - dragOrigin.x, e.screenY - dragOrigin.y);
|
||||
}
|
||||
};
|
||||
|
||||
// Always clear the cache before starting the application.
|
||||
nw.App.clearCache();
|
||||
|
||||
let nativeMenu;
|
||||
|
||||
splashWindow.on('loaded', function () {
|
||||
// Initialize the ConfigureStore
|
||||
misc.ConfigureStore.init();
|
||||
|
||||
let fixedPortCheck = misc.ConfigureStore.get('fixedPort', false);
|
||||
splashWindow.on('show', function () {
|
||||
let fixedPortCheck = configStore.get('fixedPort', false);
|
||||
if (fixedPortCheck) {
|
||||
serverPort = misc.ConfigureStore.get('portNo');
|
||||
serverPort = configStore.get('portNo');
|
||||
//Start the pgAdmin in Desktop mode.
|
||||
startDesktopMode();
|
||||
} else {
|
||||
|
@ -365,339 +374,14 @@ splashWindow.on('loaded', function () {
|
|||
})
|
||||
.catch((errCode) => {
|
||||
if (errCode === 'EADDRINUSE') {
|
||||
alert('The port specified is already in use. Please enter a free port number.');
|
||||
dialog.showErrorBox('Error', 'The port specified is already in use. Please enter a free port number.');
|
||||
} else {
|
||||
alert(errCode);
|
||||
dialog.showErrorBox('Error', errCode.toString());
|
||||
}
|
||||
splashWindow.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
splashWindow.on('close', function () {
|
||||
misc.cleanupAndQuitApp();
|
||||
splashWindow.show();
|
||||
});
|
||||
|
||||
function setNewWindowSize(){
|
||||
misc.setZoomLevelForAllWindows();
|
||||
}
|
||||
|
||||
|
||||
function addCommonMenus(menu) {
|
||||
let _menu = new gui.Menu();
|
||||
|
||||
menu.menuItems.forEach((menuItem) => {
|
||||
let submenu = getSubMenu(menuItem);
|
||||
|
||||
let _menuItem = new gui.MenuItem({
|
||||
label: menuItem.label,
|
||||
enabled: !menuItem.isDisabled,
|
||||
type: menuItem.type || 'normal',
|
||||
priority: menuItem.priority,
|
||||
...(submenu.items.length > 0) && {
|
||||
submenu: submenu,
|
||||
},
|
||||
click: function () {
|
||||
menuItem.callback();
|
||||
},
|
||||
});
|
||||
_menu.append(_menuItem);
|
||||
});
|
||||
|
||||
if (menu.name == 'file') {
|
||||
let runtimeMenu = getRuntimeMenu();
|
||||
_menu.append(runtimeMenu);
|
||||
}
|
||||
|
||||
if (menu.menuItems.length == 0) {
|
||||
let _menuItem = new gui.MenuItem({
|
||||
label: 'No object selected',
|
||||
enabled: false,
|
||||
priority: 0,
|
||||
});
|
||||
_menu.append(_menuItem);
|
||||
}
|
||||
|
||||
if (platform() == 'darwin') {
|
||||
pgAdminMainScreen.menu.insert(new gui.MenuItem({
|
||||
label: menu.label,
|
||||
name: menu.name,
|
||||
submenu: _menu,
|
||||
}), menu.index);
|
||||
} else {
|
||||
nativeMenu.append(new gui.MenuItem({
|
||||
label: menu.label,
|
||||
name: menu.name,
|
||||
submenu: _menu,
|
||||
}));
|
||||
pgAdminMainScreen.menu = nativeMenu;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getRuntimeMenu() {
|
||||
let subMenus = new gui.Menu();
|
||||
let rtmenudt = pgAdminMainScreen.window.pgAdmin.Browser.RUNTIME_MENUS_OPTIONS['runtime']
|
||||
let runtimeSubMenus = pgAdminMainScreen.window.pgAdmin.Browser.RUNTIME_MENUS_OPTIONS['runtime']['submenus']
|
||||
subMenus.append(new gui.MenuItem({
|
||||
label: runtimeSubMenus['configure'].label,
|
||||
enabled: runtimeSubMenus['configure'].enable,
|
||||
priority: runtimeSubMenus['configure'].priority,
|
||||
type: 'normal',
|
||||
checked: false,
|
||||
click: function () {
|
||||
// Create and launch new window and open pgAdmin url
|
||||
nw.Window.open('src/html/configure.html', {
|
||||
'frame': true,
|
||||
'width': 600,
|
||||
'height': 585,
|
||||
'position': 'center',
|
||||
'resizable': false,
|
||||
'focus': true,
|
||||
'show': true,
|
||||
});
|
||||
},
|
||||
}));
|
||||
subMenus.append(new gui.MenuItem({
|
||||
label: runtimeSubMenus['view_log'].label,
|
||||
enabled: runtimeSubMenus['view_log'].enable,
|
||||
priority: runtimeSubMenus['view_log'].priority,
|
||||
type: 'normal',
|
||||
checked: false,
|
||||
click: function () {
|
||||
// Create and launch new window and open pgAdmin url
|
||||
nw.Window.open('src/html/view_log.html', {
|
||||
'frame': true,
|
||||
'width': 790,
|
||||
'height': 425,
|
||||
'position': 'center',
|
||||
'resizable': false,
|
||||
'focus': true,
|
||||
'show': true,
|
||||
});
|
||||
},
|
||||
}));
|
||||
subMenus.append(new nw.MenuItem({ type: 'separator' }));
|
||||
subMenus.append(new gui.MenuItem({
|
||||
label: pgAdminMainScreen?.isFullscreen ? runtimeSubMenus['exit_full_screen'].label : runtimeSubMenus['enter_full_screen'].label,
|
||||
enabled: runtimeSubMenus['enter_full_screen'].enable,
|
||||
priority: runtimeSubMenus['enter_full_screen'].priority,
|
||||
type: 'normal',
|
||||
checked: false,
|
||||
key: runtimeSubMenus['enter_full_screen'].key,
|
||||
modifiers: runtimeSubMenus['enter_full_screen'].modifiers,
|
||||
click: function () {
|
||||
this.label = !pgAdminMainScreen?.isFullscreen ? runtimeSubMenus['exit_full_screen'].label : runtimeSubMenus['enter_full_screen'].label;
|
||||
misc.toggleFullScreen();
|
||||
},
|
||||
}));
|
||||
subMenus.append(new gui.MenuItem({
|
||||
label: runtimeSubMenus['actual_size'].label,
|
||||
enabled: runtimeSubMenus['actual_size'].enable,
|
||||
priority: runtimeSubMenus['actual_size'].priority,
|
||||
type: 'normal',
|
||||
checked: false,
|
||||
key: runtimeSubMenus['actual_size'].key,
|
||||
modifiers: runtimeSubMenus['actual_size'].modifiers,
|
||||
click: function () {
|
||||
misc.actualSize();
|
||||
},
|
||||
}));
|
||||
subMenus.append(new gui.MenuItem({
|
||||
label: runtimeSubMenus['zoom_in'].label,
|
||||
enabled: runtimeSubMenus['zoom_in'].enable,
|
||||
priority: runtimeSubMenus['zoom_in'].priority,
|
||||
type: 'normal',
|
||||
checked: false,
|
||||
key: runtimeSubMenus['zoom_in'].key,
|
||||
modifiers: runtimeSubMenus['zoom_in'].modifiers,
|
||||
click: function () {
|
||||
misc.zoomIn();
|
||||
},
|
||||
}));
|
||||
subMenus.append(new gui.MenuItem({
|
||||
label: runtimeSubMenus['zoom_out'].label,
|
||||
enabled: runtimeSubMenus['zoom_out'].enable,
|
||||
priority: runtimeSubMenus['zoom_out'].priority,
|
||||
type: 'normal',
|
||||
checked: false,
|
||||
key: runtimeSubMenus['zoom_out'].key,
|
||||
modifiers: runtimeSubMenus['zoom_out'].modifiers,
|
||||
click: function () {
|
||||
misc.zoomOut();
|
||||
},
|
||||
}));
|
||||
|
||||
let runtimeMenu = new gui.MenuItem({
|
||||
label: rtmenudt.label,
|
||||
enabled: true,
|
||||
priority: rtmenudt.priority,
|
||||
type: 'normal',
|
||||
checked: false,
|
||||
submenu: subMenus,
|
||||
})
|
||||
|
||||
return runtimeMenu;
|
||||
|
||||
}
|
||||
|
||||
function getSubMenu(menuItem) {
|
||||
let submenu = new gui.Menu();
|
||||
if (menuItem.menu_items) {
|
||||
menuItem.menu_items.forEach((item) => {
|
||||
let menuType = typeof item.checked == 'boolean' ? 'checkbox' : item.type;
|
||||
submenu.append(new gui.MenuItem({
|
||||
label: item.label,
|
||||
enabled: !item.isDisabled,
|
||||
priority: item.priority,
|
||||
type: menuType,
|
||||
checked: item.checked,
|
||||
click: function () {
|
||||
if (menuType == 'checkbox') {
|
||||
pgAdminMainScreen.menu.items.forEach(el => {
|
||||
el.submenu.items.forEach((sub) => {
|
||||
if (sub.submenu?.items?.length) {
|
||||
sub.submenu.items.forEach((m) => {
|
||||
if (m.type == 'checkbox') {
|
||||
m.checked = m.label == item.label;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
item.callback();
|
||||
},
|
||||
}));
|
||||
});
|
||||
}
|
||||
return submenu;
|
||||
}
|
||||
|
||||
function addMacMenu(menu) {
|
||||
if (menu.name == 'file' && platform() === 'darwin') {
|
||||
let rootMenu = nativeMenu.items[0].submenu;
|
||||
let indx = 0;
|
||||
menu.menuItems.forEach((menuItem) => {
|
||||
let submenu = getSubMenu(menuItem);
|
||||
|
||||
rootMenu.insert(
|
||||
new gui.MenuItem({
|
||||
label: menuItem.label,
|
||||
type: menuItem.type || 'normal',
|
||||
enabled: !menuItem.isDisabled,
|
||||
priority: menuItem.priority,
|
||||
...(submenu.items.length > 0) && {
|
||||
submenu: submenu,
|
||||
},
|
||||
click: function () {
|
||||
// Callback functions for actions
|
||||
menuItem.callback();
|
||||
},
|
||||
}), indx);
|
||||
indx++;
|
||||
});
|
||||
let runtimeMenu = getRuntimeMenu();
|
||||
rootMenu.insert(runtimeMenu, indx++);
|
||||
let separator_menu = new nw.MenuItem({ type: 'separator' });
|
||||
rootMenu.insert(separator_menu, indx);
|
||||
indx++;
|
||||
|
||||
pgAdminMainScreen.menu = nativeMenu;
|
||||
} else {
|
||||
addCommonMenus(menu)
|
||||
}
|
||||
}
|
||||
|
||||
function addOtherOsMenu(menu) {
|
||||
addCommonMenus(menu)
|
||||
}
|
||||
|
||||
|
||||
function addMenu(menu) {
|
||||
pgAdminMainScreen.isCustomMenusAdded = true;
|
||||
if (platform() === 'darwin') {
|
||||
addMacMenu(menu);
|
||||
} else {
|
||||
addOtherOsMenu(menu);
|
||||
}
|
||||
addMenuCompleted = true;
|
||||
}
|
||||
|
||||
function enableDisableMenuItem(menu, menuItem) {
|
||||
if (addMenuCompleted) {
|
||||
// Enable or Disabled specific menu item
|
||||
pgAdminMainScreen.menu.items.forEach(el => {
|
||||
if (el?.label == menu?.label) {
|
||||
el.submenu.items.forEach((sub) => {
|
||||
if (sub.label == menuItem.label) {
|
||||
sub.enabled = !menuItem.isDisabled;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateCheckedMenuItem(menuItem) {
|
||||
// check/ uncheck specific menu item
|
||||
pgAdminMainScreen.menu.items.forEach(el => {
|
||||
el.submenu.items.forEach((sub) => {
|
||||
if(sub.label == menuItem.parentMenu.label) {
|
||||
sub.submenu.items.forEach((sm)=> {
|
||||
if (sm.label == menuItem.label && sm.type == 'checkbox') {
|
||||
sm.checked = menuItem.checked
|
||||
}
|
||||
})
|
||||
} else if (sub.label == menuItem.label && type == 'checkbox') {
|
||||
sub.checked = menuItem.checked
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function refreshMenuItems(menu) {
|
||||
// Add menu item/option in specific menu.
|
||||
pgAdminMainScreen.menu.items.forEach(el => {
|
||||
if (el.label == menu.label) {
|
||||
let totalSubItems = el.submenu.items.length;
|
||||
|
||||
// Remove exisitng menu options to add new options.
|
||||
for (let i = 0; i < totalSubItems; i++) {
|
||||
el.submenu.removeAt(0);
|
||||
}
|
||||
menu.menuItems.forEach((item) => {
|
||||
|
||||
let submenu = new gui.Menu();
|
||||
if (item.menu_items) {
|
||||
item.menu_items.forEach((subItem) => {
|
||||
submenu.append(new gui.MenuItem({
|
||||
label: subItem.label,
|
||||
enabled: !subItem.isDisabled,
|
||||
priority: subItem.priority,
|
||||
type: [true, false].includes(subItem.checked) ? 'checkbox' : 'normal',
|
||||
checked: subItem.checked,
|
||||
click: function () {
|
||||
subItem.callback();
|
||||
},
|
||||
}));
|
||||
});
|
||||
}
|
||||
let _menuItem = new gui.MenuItem({
|
||||
label: item.label,
|
||||
enabled: !item.isDisabled,
|
||||
priority: item.priority,
|
||||
type: item.type,
|
||||
...(submenu.items.length > 0) && {
|
||||
submenu: submenu,
|
||||
},
|
||||
click: function () {
|
||||
item.callback();
|
||||
},
|
||||
});
|
||||
|
||||
el.submenu.append(_menuItem);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
const { contextBridge, ipcRenderer } = require('electron/renderer');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronUI', {
|
||||
onMenuClick: (callback) => ipcRenderer.on('menu-click', (_event, details) => callback(details)),
|
||||
setMenus: (menus) => {
|
||||
ipcRenderer.send('setMenus', menus);
|
||||
},
|
||||
enableDisableMenuItems: (menu, item) => {
|
||||
ipcRenderer.send('enable-disable-menu-items', menu, item);
|
||||
},
|
||||
setMenuItems: (menu, menuItems) => {
|
||||
ipcRenderer.send('set-menu-items', menu, menuItems);
|
||||
},
|
||||
showOpenDialog: (options) => ipcRenderer.invoke('showOpenDialog', options),
|
||||
showSaveDialog: (options) => ipcRenderer.invoke('showSaveDialog', options),
|
||||
log: (text)=> ipcRenderer.send('log', text),
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
const misc = require('../js/misc.js');
|
||||
|
||||
// Get the window object of server error window
|
||||
let gui = require('nw.gui');
|
||||
let errorWindow = gui.Window.get();
|
||||
|
||||
errorWindow.on('loaded', function() {
|
||||
document.getElementById('server_error_label').innerHTML = 'The pgAdmin 4 server could not be contacted:';
|
||||
document.getElementById('server_error_log').innerHTML = misc.readServerLog();
|
||||
document.getElementById('btnConfigure').addEventListener('click', function() {
|
||||
nw.Window.open('src/html/configure.html', {
|
||||
'frame': true,
|
||||
'width': 600,
|
||||
'height': 420,
|
||||
'position': 'center',
|
||||
'resizable': false,
|
||||
'focus': true,
|
||||
'show': true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
errorWindow.on('close', function() {
|
||||
misc.cleanupAndQuitApp();
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
const misc = require('../js/misc.js');
|
||||
|
||||
// Get the window object of view log window
|
||||
let gui = require('nw.gui');
|
||||
let logWindow = gui.Window.get();
|
||||
|
||||
logWindow.on('loaded', function() {
|
||||
document.getElementById('status-text').innerHTML = '';
|
||||
document.getElementById('server_log_label').innerHTML = 'Server Log: ' + '(' + misc.getServerLogFile() + ')';
|
||||
document.getElementById('server_log').innerHTML = misc.readServerLog();
|
||||
document.getElementById('btnReload').addEventListener('click', function() {
|
||||
document.getElementById('server_log').innerHTML = 'Loading logs...';
|
||||
setTimeout(function() {
|
||||
document.getElementById('server_log').innerHTML = misc.readServerLog();
|
||||
}, 500);
|
||||
document.getElementById('status-text').innerHTML = 'Logs reloaded successfully';
|
||||
});
|
||||
});
|
1439
runtime/yarn.lock
1439
runtime/yarn.lock
File diff suppressed because it is too large
Load Diff
|
@ -18,10 +18,10 @@ from pgadmin.utils.menu import MenuItem
|
|||
from pgadmin.utils.constants import MIMETYPE_APP_JS
|
||||
from pgadmin.utils.ajax import make_json_response
|
||||
import config
|
||||
import httpagentparser
|
||||
from pgadmin.model import User
|
||||
from user_agents import parse
|
||||
import platform
|
||||
import re
|
||||
|
||||
MODULE_NAME = 'about'
|
||||
|
||||
|
@ -59,11 +59,11 @@ def index():
|
|||
"""Render the about box."""
|
||||
info = {}
|
||||
# Get OS , NW.js, Browser details
|
||||
browser, os_details, nwjs_version = detect_browser(request)
|
||||
browser, os_details, electron_version = detect_browser(request)
|
||||
admin = is_admin(current_user.email)
|
||||
|
||||
if nwjs_version:
|
||||
info['nwjs'] = nwjs_version
|
||||
if electron_version:
|
||||
info['electron'] = electron_version
|
||||
|
||||
if config.SERVER_MODE:
|
||||
info['app_mode'] = gettext('Server')
|
||||
|
@ -119,32 +119,19 @@ def is_admin(load_user):
|
|||
|
||||
def detect_browser(request):
|
||||
"""This function returns the browser and os details"""
|
||||
nwjs_version = None
|
||||
electron_version = None
|
||||
agent = request.environ.get('HTTP_USER_AGENT')
|
||||
os_details = parse(platform.platform()).ua_string
|
||||
|
||||
if 'Nwjs' in agent:
|
||||
agent = agent.split('-')
|
||||
nwjs_version = agent[0].split(':')[1]
|
||||
browser = 'Chromium' + ' ' + agent[2]
|
||||
if 'Electron' in agent:
|
||||
electron_version = re.findall('Electron/([\\d.]+\\d+)', agent)[0]
|
||||
|
||||
else:
|
||||
browser = httpagentparser.detect(agent)
|
||||
browser = re.findall(
|
||||
'(opera|chrome|safari|firefox|msie|trident(?=/))/?\\s*([\\d.]+\\d+)',
|
||||
agent, re.IGNORECASE)
|
||||
if not browser:
|
||||
browser = agent.split('/')[0]
|
||||
else:
|
||||
browser = browser['browser']['name'] + ' ' + browser['browser'][
|
||||
'version']
|
||||
browser = " ".join(browser[0])
|
||||
|
||||
return browser, os_details, nwjs_version
|
||||
|
||||
|
||||
@blueprint.route("/about.js")
|
||||
@pga_login_required
|
||||
def script():
|
||||
"""render the required javascript"""
|
||||
return Response(
|
||||
response=render_template("about/about.js", _=gettext),
|
||||
status=200,
|
||||
mimetype=MIMETYPE_APP_JS
|
||||
)
|
||||
return browser, os_details, electron_version
|
||||
|
|
|
@ -65,13 +65,13 @@ export default function AboutComponent() {
|
|||
<InputLabel>{aboutData.current_user}</InputLabel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{ aboutData.nwjs &&
|
||||
{ aboutData.electron &&
|
||||
<Grid container spacing={0} style={{marginBottom: '8px'}}>
|
||||
<Grid item lg={3} md={3} sm={3} xs={12}>
|
||||
<InputLabel style={{fontWeight: 'bold'}}>{gettext('NW.js Version')}</InputLabel>
|
||||
<InputLabel style={{fontWeight: 'bold'}}>{gettext('Electron Version')}</InputLabel>
|
||||
</Grid>
|
||||
<Grid item lg={9} md={9} sm={9} xs={12}>
|
||||
<InputLabel>{aboutData.nwjs}</InputLabel>
|
||||
<InputLabel>{aboutData.electron}</InputLabel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ const MAIN_MENUS = [
|
|||
];
|
||||
|
||||
let { name: browser } = getBrowser();
|
||||
if (browser == 'Nwjs') {
|
||||
if (browser == 'Electron') {
|
||||
let controlKey = isMac() ? 'cmd' : 'ctrl';
|
||||
let fullScreenKey = isMac() ? 'F' : 'F10';
|
||||
|
||||
|
@ -47,9 +47,31 @@ if (browser == 'Nwjs') {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export default class MainMenuFactory {
|
||||
static electronCallbacks = {};
|
||||
|
||||
static toElectron() {
|
||||
// we support 2 levels of submenu
|
||||
return pgAdmin.Browser.MainMenus.map((m)=>{
|
||||
return {
|
||||
...m.serialize(),
|
||||
submenu: m.menuItems.map((sm)=>{
|
||||
const smName = `${m.name}_${sm.name}`;
|
||||
MainMenuFactory.electronCallbacks[smName] = sm.callback;
|
||||
return {
|
||||
...sm.serialize(),
|
||||
submenu: sm.getMenuItems()?.map((smsm)=>{
|
||||
MainMenuFactory.electronCallbacks[`${smName}_${smsm.name}`] = sm.callback;
|
||||
return {
|
||||
...smsm.serialize(),
|
||||
};
|
||||
})
|
||||
};
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
static createMainMenus() {
|
||||
pgAdmin.Browser.MainMenus = [];
|
||||
MAIN_MENUS.forEach((_menu) => {
|
||||
|
@ -70,6 +92,12 @@ export default class MainMenuFactory {
|
|||
});
|
||||
|
||||
pgAdmin.Browser.enable_disable_menus();
|
||||
|
||||
window.electronUI?.onMenuClick((menuName)=>{
|
||||
MainMenuFactory.electronCallbacks[menuName]?.();
|
||||
});
|
||||
|
||||
window.electronUI?.setMenus(MainMenuFactory.toElectron());
|
||||
}
|
||||
|
||||
static getSeparator(label, priority) {
|
||||
|
@ -78,7 +106,8 @@ export default class MainMenuFactory {
|
|||
|
||||
static refreshMainMenuItems(menu, menuItems) {
|
||||
menu.setMenuItems(menuItems);
|
||||
pgAdmin.Browser.Events.trigger('pgadmin:nw-refresh-menu-item', menu);
|
||||
window.electronUI?.setMenus(MainMenuFactory.toElectron());
|
||||
pgAdmin.Browser.Events.trigger('pgadmin:nw-refresh-menu-item', pgAdmin.Browser.MainMenus);
|
||||
}
|
||||
|
||||
static createMenuItem(options) {
|
||||
|
@ -102,6 +131,7 @@ export default class MainMenuFactory {
|
|||
}
|
||||
}}, (menu, item)=> {
|
||||
pgAdmin.Browser.Events.trigger('pgadmin:nw-enable-disable-menu-items', menu, item);
|
||||
window.electronUI?.enableDisableMenuItems(menu?.serialize(), item?.serialize());
|
||||
}, (item) => {
|
||||
pgAdmin.Browser.Events.trigger('pgadmin:nw-update-checked-menu-item', item);
|
||||
});
|
||||
|
|
|
@ -3,17 +3,29 @@
|
|||
{% block title %}{{ config.APP_NAME }}{% endblock %}
|
||||
|
||||
{% block init_script %}
|
||||
window.hookConsole = function(callback) {
|
||||
function parseConsoleArgs(args) {
|
||||
const retData = Array.from(args).map(arg => {
|
||||
try {
|
||||
if(arg.stack) return arg.stack;
|
||||
return JSON.stringify(arg);
|
||||
} catch (e) {
|
||||
return arg
|
||||
}
|
||||
});
|
||||
return retData?.join(' ');
|
||||
}
|
||||
|
||||
for (const method of ['log', 'error']) {
|
||||
const nativeMethod = window.console[method];
|
||||
window.console[method] = function () {
|
||||
nativeMethod.apply(this, arguments);
|
||||
setTimeout(()=>{
|
||||
callback(method, arguments);
|
||||
window.electronUI?.log(`--------------[UI ${method}]---------------
|
||||
${parseConsoleArgs(arguments)}
|
||||
------------[UI End]----------------`);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
require(
|
||||
['sources/generated/app.bundle', 'sources/generated/browser_nodes'],
|
||||
|
|
|
@ -85,30 +85,35 @@ export default class FileManagerModule {
|
|||
});
|
||||
}
|
||||
|
||||
showNative(params, onOK, onCancel) {
|
||||
// https://docs.nwjs.io/en/latest/References/Changes%20to%20DOM/
|
||||
let fileEle = document.createElement('input');
|
||||
let accept = params.supported_types?.map((v)=>(v=='*' ? '' : `.${v}`))?.join(',');
|
||||
fileEle.setAttribute('type', 'file');
|
||||
fileEle.setAttribute('accept', accept);
|
||||
fileEle.onchange = (e)=>{
|
||||
if(e.target.value) {
|
||||
onOK?.(e.target.value);
|
||||
} else {
|
||||
onCancel?.();
|
||||
}
|
||||
};
|
||||
async showNative(params, onOK, onCancel) {
|
||||
let res;
|
||||
let options = {};
|
||||
|
||||
options['filters'] = params.supported_types?.map((v)=>(
|
||||
v=='*' ? {name: 'All Files', extensions: ['*']} :
|
||||
{name: `${v.toUpperCase()} File .${v}`, extensions:[v]}
|
||||
));
|
||||
|
||||
if(params.dialog_type == 'create_file') {
|
||||
fileEle.setAttribute('nwsaveas', '');
|
||||
} else if(params.dialog_type == 'select_folder') {
|
||||
fileEle.setAttribute('nwdirectory', '');
|
||||
res = await window.electronUI.showSaveDialog(options);
|
||||
} else {
|
||||
options['properties'] = ['openFile'];
|
||||
if(params.dialog_type == 'select_folder') {
|
||||
options['properties'] = ['openDirectory'];
|
||||
}
|
||||
res = await window.electronUI.showOpenDialog(options);
|
||||
}
|
||||
|
||||
if(res.canceled) {
|
||||
onCancel?.();
|
||||
} else {
|
||||
onOK?.(res.filePaths[0]);
|
||||
}
|
||||
fileEle.dispatchEvent(new MouseEvent('click'));
|
||||
}
|
||||
|
||||
show(params, onOK, onCancel, modalObj) {
|
||||
let {name: browser} = getBrowser();
|
||||
if(browser == 'Nwjs') {
|
||||
if(browser == 'Electron') {
|
||||
try {
|
||||
this.showNative(params, onOK, onCancel);
|
||||
} catch {
|
||||
|
|
|
@ -111,8 +111,8 @@ export default function BrowserComponent({pgAdmin}) {
|
|||
<PgAdminContext.Provider value={pgAdmin}>
|
||||
<ModalProvider>
|
||||
<NotifierProvider pgAdmin={pgAdmin} onReady={()=>setUiReady(true)}/>
|
||||
{browser != 'Nwjs' && <AppMenuBar />}
|
||||
<div style={{height: (browser != 'Nwjs' ? 'calc(100% - 30px)' : '100%')}}>
|
||||
{browser != 'Electron' && <AppMenuBar />}
|
||||
<div style={{height: (browser != 'Electron' ? 'calc(100% - 30px)' : '100%')}}>
|
||||
<Layout
|
||||
getLayoutInstance={(obj)=>{
|
||||
pgAdmin.Browser.docker = obj;
|
||||
|
|
|
@ -24,6 +24,16 @@ export default class Menu {
|
|||
return menuObj;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return {
|
||||
id: this.id,
|
||||
label: this.label,
|
||||
name: this.name,
|
||||
index: this.index,
|
||||
addSepratior: this.addSepratior,
|
||||
};
|
||||
}
|
||||
|
||||
addMenuItem(menuItem, index=null) {
|
||||
if (menuItem instanceof MenuItem) {
|
||||
menuItem.parentMenu = this;
|
||||
|
@ -116,7 +126,6 @@ export class MenuItem {
|
|||
url: '#',
|
||||
target: '_self',
|
||||
enable: true,
|
||||
type: 'normal'
|
||||
};
|
||||
_.extend(this, defaults, _.pick(options, menu_opts));
|
||||
if (!this.callback) {
|
||||
|
@ -136,6 +145,17 @@ export class MenuItem {
|
|||
return MenuItem(options);
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return {
|
||||
name: this.name,
|
||||
label: this.label,
|
||||
enabled: !this.isDisabled,
|
||||
priority: this.priority,
|
||||
type: [true, false].includes(this.checked) ? 'checkbox' : this.type,
|
||||
checked: this.checked,
|
||||
};
|
||||
}
|
||||
|
||||
change_checked(isChecked) {
|
||||
this.checked = isChecked;
|
||||
this.changeChecked?.(this);
|
||||
|
|
|
@ -377,14 +377,17 @@ export function evalFunc(obj, func, ...param) {
|
|||
}
|
||||
|
||||
export function getBrowser() {
|
||||
if(navigator.userAgent.indexOf('Electron') >= 0) {
|
||||
return {name: 'Electron', version: navigator.userAgent.match(/Electron\/([\d\.]+\d+)/)[1]};
|
||||
}
|
||||
|
||||
let ua=navigator.userAgent,tem,M=(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i).exec(ua) || [];
|
||||
if(/trident/i.test(M[1])) {
|
||||
tem=/\brv[ :]+(\d+)/g.exec(ua) || [];
|
||||
return {name:'IE', version:(tem[1]||'')};
|
||||
}
|
||||
if(ua.startsWith('Nwjs')) {
|
||||
let nwjs = ua.split('-')[0]?.split(':');
|
||||
return {name:nwjs[0], version: nwjs[1]};
|
||||
if(ua.indexOf('Electron') >= 0) {
|
||||
return {name: 'Electron', version: ua.match(/Electron\/([\d\.]+\d+)/)[1]};
|
||||
}
|
||||
|
||||
if(M[1]==='Chrome') {
|
||||
|
|
|
@ -24,7 +24,7 @@ import { Box } from '@mui/material';
|
|||
import { getDatabaseLabel, getTitle, setQueryToolDockerTitle } from '../sqleditor_title';
|
||||
import gettext from 'sources/gettext';
|
||||
import NewConnectionDialog from './dialogs/NewConnectionDialog';
|
||||
import { evalFunc } from '../../../../../static/js/utils';
|
||||
import { evalFunc, getBrowser } from '../../../../../static/js/utils';
|
||||
import { Notifications } from './sections/Notifications';
|
||||
import MacrosDialog from './dialogs/MacrosDialog';
|
||||
import FilterDialog from './dialogs/FilterDialog';
|
||||
|
@ -405,6 +405,12 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
|||
});
|
||||
};
|
||||
|
||||
const onBeforeUnloadElectron = (e)=>{
|
||||
e.preventDefault();
|
||||
e.returnValue = 'prevent';
|
||||
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_DATA_CLOSE);
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
getSQLScript();
|
||||
initializeQueryTool();
|
||||
|
@ -418,7 +424,14 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
|||
});
|
||||
|
||||
eventBus.current.registerListener(QUERY_TOOL_EVENTS.FORCE_CLOSE_PANEL, ()=>{
|
||||
if(getBrowser().name == 'Electron' && qtState.is_new_tab) {
|
||||
window.removeEventListener('beforeunload', onBeforeUnloadElectron);
|
||||
// somehow window.close was not working may becuase the removeEventListener
|
||||
// was not completely executed. Add timeout.
|
||||
setTimeout(()=>window.close(), 50);
|
||||
} else {
|
||||
qtPanelDocker.close(qtPanelId, true);
|
||||
}
|
||||
});
|
||||
|
||||
qtPanelDocker.eventBus.registerListener(LAYOUT_EVENTS.CLOSING, (id)=>{
|
||||
|
@ -561,7 +574,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
|||
const events = [
|
||||
[QUERY_TOOL_EVENTS.TRIGGER_LOAD_FILE, ()=>{
|
||||
let fileParams = {
|
||||
'supported_types': ['*', 'sql'], // file types allowed
|
||||
'supported_types': ['sql', '*'], // file types allowed
|
||||
'dialog_type': 'select_file', // open select file dialog
|
||||
};
|
||||
pgAdmin.Tools.FileManager.show(fileParams, (fileName, storage)=>{
|
||||
|
@ -573,7 +586,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
|||
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.SAVE_FILE, qtState.current_file);
|
||||
} else {
|
||||
let fileParams = {
|
||||
'supported_types': ['*', 'sql'],
|
||||
'supported_types': ['sql', '*'],
|
||||
'dialog_type': 'create_file',
|
||||
'dialog_title': 'Save File',
|
||||
'btn_primary': 'Save',
|
||||
|
@ -632,13 +645,18 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
|||
|
||||
useEffect(()=> {
|
||||
// Add beforeunload event if "Confirm on close or refresh" option is enabled in the preferences.
|
||||
if(getBrowser().name == 'Electron') {
|
||||
window.addEventListener('beforeunload', onBeforeUnloadElectron);
|
||||
} else {
|
||||
if(qtState.preferences.browser.confirm_on_refresh_close){
|
||||
window.addEventListener('beforeunload', onBeforeUnload);
|
||||
} else {
|
||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', onBeforeUnloadElectron);
|
||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||
};
|
||||
}, [qtState.preferences.browser]);
|
||||
|
|
Loading…
Reference in New Issue