Replace pgAdmin NW.js container with Electron container. #7494
parent
4457a6a6a1
commit
91eb60a363
|
@ -9,6 +9,7 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
.sonarlint
|
||||||
.yarn
|
.yarn
|
||||||
/*.diff
|
/*.diff
|
||||||
/*.patch
|
/*.patch
|
||||||
|
@ -38,6 +39,7 @@ runtime/pgAdmin4.app/
|
||||||
runtime/pgAdmin4.pro.user*
|
runtime/pgAdmin4.pro.user*
|
||||||
runtime/pgAdmin4_resource.rc
|
runtime/pgAdmin4_resource.rc
|
||||||
runtime/release/
|
runtime/release/
|
||||||
|
runtime/dev_config.json
|
||||||
runtime/ui_BrowserWindow.h
|
runtime/ui_BrowserWindow.h
|
||||||
web/config_local.py
|
web/config_local.py
|
||||||
web/pgadmin/misc/themes/pgadmin.themes.json
|
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
|
RD /Q /S "%BUILDROOT%\docs\en_US\html\_sources" 1> nul 2>&1
|
||||||
|
|
||||||
ECHO Staging runtime components...
|
ECHO Staging runtime components...
|
||||||
XCOPY /S /I /E /H /Y "%WD%\runtime\assets" "%BUILDROOT%\runtime\assets" > nul || EXIT /B 1
|
MKDIR "%BUILDROOT%\runtime\resources\app"
|
||||||
XCOPY /S /I /E /H /Y "%WD%\runtime\src" "%BUILDROOT%\runtime\src" > nul || EXIT /B 1
|
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
|
COPY "%WD%\runtime\package.json" "%BUILDROOT%\runtime\resources\app\" > nul || EXIT /B 1
|
||||||
CD "%BUILDROOT%\runtime\"
|
CD "%BUILDROOT%\runtime\resources\app\"
|
||||||
CALL yarn install --production=true || EXIT /B 1
|
CALL yarn install --production=true || EXIT /B 1
|
||||||
|
|
||||||
ECHO Downloading NWjs to %TMPDIR%...
|
ECHO Downloading Electron to %TMPDIR%...
|
||||||
REM Get a fresh copy of nwjs.
|
REM Get a fresh copy of electron.
|
||||||
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
|
|
||||||
|
|
||||||
REM WGET
|
REM WGET
|
||||||
REM Comment out the below line as the latest version having some
|
FOR /f "tokens=2 delims='" %%i IN ('yarn info electron ^| findstr "latest: "') DO SET "ELECTRON_VERSION=%%i"
|
||||||
REM problem https://github.com/nwjs/nw.js/issues/7964, so for the time being
|
:GET_NW
|
||||||
REM hardcoded the version to 0.77.0
|
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"
|
||||||
REM FOR /f "tokens=2 delims='" %%i IN ('yarn info nw ^| findstr "latest: "') DO SET "NW_VERSION=%%i"
|
IF %ERRORLEVEL% NEQ 0 GOTO GET_NW
|
||||||
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
|
|
||||||
|
|
||||||
SET "NW_VERSION=0.77.0"
|
MKDIR "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64" || EXIT /B 1
|
||||||
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%\electron-v%ELECTRON_VERSION%-win32-x64" -xvf "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64.zip" || EXIT /B 1
|
||||||
|
|
||||||
tar -C "%TMPDIR%" -xvf "%TMPDIR%\nwjs-v%NW_VERSION%-win-x64.zip" || EXIT /B 1
|
|
||||||
REM WGET END
|
REM WGET END
|
||||||
|
|
||||||
REM YARN
|
REM XCOPY
|
||||||
REM XCOPY /S /I /E /H /Y "%TMPDIR%\node_modules\nw\nwjs\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
XCOPY /S /I /E /H /Y "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||||
REM YARN END
|
REM XCOPY END
|
||||||
|
|
||||||
REM WGET
|
MOVE "%BUILDROOT%\runtime\electron.exe" "%BUILDROOT%\runtime\pgAdmin4.exe"
|
||||||
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"
|
|
||||||
ECHO Attempting to sign the 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"
|
CALL "%PGADMIN_SIGNTOOL_DIR%\signtool.exe" sign /tr http://timestamp.digicert.com "%BUILDROOT%\runtime\pgAdmin4.exe"
|
||||||
IF %ERRORLEVEL% NEQ 0 (
|
IF %ERRORLEVEL% NEQ 0 (
|
||||||
|
@ -310,8 +297,13 @@ REM Main build sequence Ends
|
||||||
)
|
)
|
||||||
|
|
||||||
ECHO Replacing executable icon...
|
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...
|
ECHO Staging PostgreSQL components...
|
||||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libpq.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\libpq.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||||
|
|
|
@ -133,55 +133,47 @@ _create_python_virtualenv() {
|
||||||
_build_runtime() {
|
_build_runtime() {
|
||||||
echo "Assembling the desktop runtime..."
|
echo "Assembling the desktop runtime..."
|
||||||
|
|
||||||
# Get a fresh copy of nwjs.
|
# Get a fresh copy of electron.
|
||||||
# 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.
|
|
||||||
|
|
||||||
# YARN:
|
ELECTRON_ARCH="x64"
|
||||||
# yarn add --cwd "${BUILDROOT}" nw
|
if [ "$(uname -m)" == "arm64" ]; then
|
||||||
# YARN END
|
ELECTRON_ARCH="arm64"
|
||||||
|
fi
|
||||||
|
|
||||||
# WGET:
|
ELECTRON_VERSION=$(yarn info electron | grep latest | awk -F "'" '{ print $2}')
|
||||||
# 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"
|
|
||||||
|
|
||||||
pushd "${BUILDROOT}" > /dev/null || exit
|
pushd "${BUILDROOT}" > /dev/null || exit
|
||||||
while true;do
|
while true;do
|
||||||
wget "https://dl.nwjs.io/v${NW_VERSION}/nwjs-v${NW_VERSION}-linux-x64.tar.gz" && break
|
wget "https://github.com/electron/electron/releases/download/v${ELECTRON_VERSION}/electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}.zip" && break
|
||||||
rm "nwjs-v${NW_VERSION}-linux-x64.tar.gz"
|
rm "electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}.zip"
|
||||||
done
|
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
|
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"
|
mkdir -p "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||||
|
|
||||||
# The chmod command below is needed to fix the permission issue of
|
# The chmod command below is needed to fix the permission issue of
|
||||||
# the NWjs binaries and files.
|
# the NWjs binaries and files.
|
||||||
# Change the permission for others and group the same as the owner
|
# 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
|
# 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:
|
BUNDLEDIR="${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||||
# cp -r "${BUILDROOT}/node_modules/nw/nwjs"/* "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
|
||||||
# YARN END
|
|
||||||
|
|
||||||
# WGET:
|
# 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
|
# 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"
|
mkdir -p "${BUNDLEDIR}/resources/app"
|
||||||
cp -r "${SOURCEDIR}/runtime/src" "${DESKTOPROOT}/usr/${APP_NAME}/bin/src"
|
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/"
|
cp "${SOURCEDIR}/runtime/package.json" "${BUNDLEDIR}/resources/app"
|
||||||
yarn --cwd "${DESKTOPROOT}/usr/${APP_NAME}/bin" install --production=true
|
yarn --cwd "${BUNDLEDIR}/resources/app" install --production=true
|
||||||
|
|
||||||
# Create the icon
|
# Create the icon
|
||||||
mkdir -p "${DESKTOPROOT}/usr/share/icons/hicolor/128x128/apps/"
|
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..."
|
echo "Assembling the runtime environment..."
|
||||||
|
|
||||||
test -d "${BUILD_ROOT}" || mkdir "${BUILD_ROOT}"
|
test -d "${BUILD_ROOT}" || mkdir "${BUILD_ROOT}"
|
||||||
|
# Get a fresh copy of electron
|
||||||
# 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.
|
|
||||||
|
|
||||||
# uname -m returns "x86_64" on Intel, but we need "x64"
|
# uname -m returns "x86_64" on Intel, but we need "x64"
|
||||||
NW_ARCH="x64"
|
ELECTRON_ARCH="x64"
|
||||||
if [ "$(uname -m)" == "arm64" ]; then
|
if [ "$(uname -m)" == "arm64" ]; then
|
||||||
NW_ARCH="arm64"
|
ELECTRON_ARCH="arm64"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# YARN:
|
ELECTRON_VERSION=$(yarn info electron | grep latest | awk -F "'" '{ print $2}')
|
||||||
# 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"
|
|
||||||
|
|
||||||
pushd "${BUILD_ROOT}" > /dev/null || exit
|
pushd "${BUILD_ROOT}" > /dev/null || exit
|
||||||
while true;do
|
while true;do
|
||||||
wget "https://dl.nwjs.io/v${NW_VERSION}/nwjs-v${NW_VERSION}-osx-${NW_ARCH}.zip" && break
|
wget "https://github.com/electron/electron/releases/download/v${ELECTRON_VERSION}/electron-v${ELECTRON_VERSION}-darwin-${ELECTRON_ARCH}.zip" && break
|
||||||
rm "nwjs-v${NW_VERSION}-osx-${NW_ARCH}.zip"
|
rm "electron-v${ELECTRON_VERSION}-darwin-${ELECTRON_ARCH}.zip"
|
||||||
done
|
done
|
||||||
unzip "nwjs-v${NW_VERSION}-osx-${NW_ARCH}.zip"
|
unzip "electron-v${ELECTRON_VERSION}-darwin-${ELECTRON_ARCH}.zip"
|
||||||
popd > /dev/null || exit
|
popd > /dev/null || exit
|
||||||
# WGET END
|
# WGET END
|
||||||
|
|
||||||
# YARN:
|
|
||||||
# cp -R "${BUILD_ROOT}/node_modules/nw/nwjs/nwjs.app" "${BUILD_ROOT}/"
|
|
||||||
# YARN END
|
|
||||||
|
|
||||||
# WGET:
|
mv "${BUILD_ROOT}/Electron.app" "${BUNDLE_DIR}"
|
||||||
cp -R "${BUILD_ROOT}/nwjs-v${NW_VERSION}-osx-${NW_ARCH}"/nwjs.app "${BUILD_ROOT}/"
|
find "${BUNDLE_DIR}" -exec touch {} \;
|
||||||
# WGET END
|
|
||||||
|
|
||||||
mv "${BUILD_ROOT}/nwjs.app" "${BUNDLE_DIR}"
|
|
||||||
|
|
||||||
# Copy in the runtime code
|
# Copy in the runtime code
|
||||||
mkdir "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
mkdir "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||||
cp -R "${SOURCE_DIR}/runtime/assets" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
cp -R "${SOURCE_DIR}/runtime/assets" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||||
cp -R "${SOURCE_DIR}/runtime/src" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
cp -R "${SOURCE_DIR}/runtime/src" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||||
cp "${SOURCE_DIR}/runtime/package.json" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
cp "${SOURCE_DIR}/runtime/package.json" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||||
cp "${SOURCE_DIR}/runtime/.yarnrc.yml" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
cp "${SOURCE_DIR}/runtime/.yarnrc.yml" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||||
|
|
||||||
# Install the runtime node_modules, then replace the package.json
|
# 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 berry
|
||||||
yarn set version 3
|
yarn set version 3
|
||||||
yarn plugin import workspace-tools
|
yarn plugin import workspace-tools
|
||||||
|
@ -178,7 +159,6 @@ _fixup_imports() {
|
||||||
# Find all the files that may need tweaks
|
# Find all the files that may need tweaks
|
||||||
TODO=$(find . -perm +0111 -type f -exec file "{}" \; | \
|
TODO=$(find . -perm +0111 -type f -exec file "{}" \; | \
|
||||||
grep -v "Frameworks/Python.framework" | \
|
grep -v "Frameworks/Python.framework" | \
|
||||||
grep -v "Frameworks/nwjs" | \
|
|
||||||
grep -E "Mach-O 64-bit" | \
|
grep -E "Mach-O 64-bit" | \
|
||||||
awk -F ':| ' '{ORS=" "; print $1}' | \
|
awk -F ':| ' '{ORS=" "; print $1}' | \
|
||||||
uniq)
|
uniq)
|
||||||
|
@ -273,10 +253,19 @@ _complete_bundle() {
|
||||||
sed -i '' "s/%APPNAME%/${APP_NAME}/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
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/%APPVER%/${APP_LONG_VERSION}/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
||||||
sed -i '' "s/%APPID%/org.pgadmin.pgadmin4/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}"
|
# Rename helper execs and Update the plist
|
||||||
sed -i '' 's/NSHumanReadableCopyright =.*/NSHumanReadableCopyright = "Copyright (C) 2013 - 2024, The pgAdmin Development Team";/g' "${FILE}"
|
for helper_exec in "Electron Helper" "Electron Helper (Renderer)" "Electron Helper (Plugin)" "Electron Helper (GPU)"
|
||||||
echo CFBundleDisplayName = \""${APP_NAME}"\"\; >> "${FILE}"
|
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
|
done
|
||||||
|
|
||||||
# PkgInfo
|
# PkgInfo
|
||||||
|
@ -286,10 +275,10 @@ _complete_bundle() {
|
||||||
cp pgAdmin4.icns "${BUNDLE_DIR}/Contents/Resources/app.icns"
|
cp pgAdmin4.icns "${BUNDLE_DIR}/Contents/Resources/app.icns"
|
||||||
|
|
||||||
# Rename the executable
|
# 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
|
# 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
|
# Import the dependencies, and rewrite any library references
|
||||||
_fixup_imports "${BUNDLE_DIR}"
|
_fixup_imports "${BUNDLE_DIR}"
|
||||||
|
|
|
@ -41,7 +41,6 @@ sshtunnel==0.*
|
||||||
ldap3==2.*
|
ldap3==2.*
|
||||||
gssapi==1.8.*
|
gssapi==1.8.*
|
||||||
eventlet==0.36.1
|
eventlet==0.36.1
|
||||||
httpagentparser==1.9.*
|
|
||||||
user-agents==2.2.0
|
user-agents==2.2.0
|
||||||
pywinpty==2.0.*; sys_platform=="win32"
|
pywinpty==2.0.*; sys_platform=="win32"
|
||||||
Authlib==1.3.*; python_version > '3.7'
|
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
|
// This software is released under the PostgreSQL Licence
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
|
import globals from 'globals';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
|
||||||
module.exports = {
|
export default [
|
||||||
'env': {
|
js.configs.recommended,
|
||||||
'browser': true,
|
{
|
||||||
'es6': true,
|
files: ['**/*.js'],
|
||||||
'amd': true,
|
ignores: [
|
||||||
},
|
'generated',
|
||||||
'extends': [
|
'node_modules',
|
||||||
'eslint:recommended',
|
'vendor',
|
||||||
|
'templates/',
|
||||||
|
'templates\\',
|
||||||
|
'ycache',
|
||||||
],
|
],
|
||||||
'parserOptions': {
|
languageOptions: {
|
||||||
'ecmaVersion': 2018,
|
ecmaVersion: 2022,
|
||||||
"sourceType": "module",
|
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': {
|
'rules': {
|
||||||
'indent': [
|
'indent': [
|
||||||
'error',
|
'error',
|
||||||
2
|
2,
|
||||||
],
|
],
|
||||||
'linebreak-style': 0,
|
'linebreak-style': 0,
|
||||||
'quotes': [
|
'quotes': [
|
||||||
'error',
|
'error',
|
||||||
'single'
|
'single',
|
||||||
],
|
],
|
||||||
'semi': [
|
'semi': [
|
||||||
'error',
|
'error',
|
||||||
'always'
|
'always',
|
||||||
],
|
],
|
||||||
'comma-dangle': [
|
'comma-dangle': [
|
||||||
'error',
|
'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
|
// We need to exclude below for RegEx case
|
||||||
"no-useless-escape": 0,
|
'no-useless-escape': 0,
|
||||||
},
|
},
|
||||||
};
|
},
|
||||||
|
];
|
||||||
|
|
|
@ -1,37 +1,24 @@
|
||||||
{
|
{
|
||||||
"name": "pgadmin4",
|
"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.",
|
"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",
|
"repository": "https://github.com/postgres/pgadmin4.git",
|
||||||
"author": "pgAdmin Development Team (https://www.pgadmin.org/)",
|
"author": "pgAdmin Development Team (https://www.pgadmin.org/)",
|
||||||
"license": "PostgreSQL",
|
"license": "PostgreSQL",
|
||||||
"chromium-args": "--disable-popup-blocking --disable-gpu --disable-devtools",
|
"type": "module",
|
||||||
"user-agent": "Nwjs:%nwver-%osinfo-%chromium_ver",
|
"main": "src/js/pgadmin.js",
|
||||||
"nodejs": true,
|
"scripts": {
|
||||||
"window": {
|
"start": "electron .",
|
||||||
"width": 750,
|
"linter": "yarn run eslint -c .eslintrc.js ."
|
||||||
"height": 600,
|
|
||||||
"toolbar": false,
|
|
||||||
"fullscreen": false,
|
|
||||||
"frame": false,
|
|
||||||
"show": true,
|
|
||||||
"position": "center",
|
|
||||||
"always-on-top": true,
|
|
||||||
"icon": "assets/pgAdmin4.png"
|
|
||||||
},
|
},
|
||||||
"icons": {
|
"packageManager": "yarn@3.8.2",
|
||||||
"256": "assets/pgAdmin4.png"
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.5.0",
|
||||||
|
"electron": "^30.0.5",
|
||||||
|
"eslint": "^9.5.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"bootstrap": "^4.5.3"
|
"bootstrap": "^4.5.3",
|
||||||
},
|
"electron-store": "^9.0.0"
|
||||||
"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"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<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"/>
|
<link rel="stylesheet" href="../css/pgadmin-desktop.css"/>
|
||||||
<style>
|
<style>
|
||||||
body, html {
|
body, html {
|
||||||
|
@ -76,6 +76,99 @@
|
||||||
<button id="btnSave" type="submit" class="btn btn-primary" disabled>Save</button>
|
<button id="btnSave" type="submit" class="btn btn-primary" disabled>Save</button>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -31,6 +31,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
-webkit-app-region: drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader-text {
|
.loader-text {
|
||||||
|
@ -50,9 +51,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="bg">
|
<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>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript" src="../js/pgadmin.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -31,6 +31,19 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</body>
|
||||||
</html>
|
</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');
|
import fs from 'fs';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const net = require('net');
|
import net from 'net';
|
||||||
const {platform, homedir} = require('os');
|
import {platform} from 'os';
|
||||||
|
import { app } from 'electron';
|
||||||
|
|
||||||
let pgadminServerProcess = null;
|
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
|
// This function is used to check whether directory is present or not
|
||||||
// if not present then create it recursively
|
// if not present then create it recursively
|
||||||
|
@ -28,26 +25,31 @@ const createDir = (dirName) => {
|
||||||
|
|
||||||
const insideFlatpak = () => {
|
const insideFlatpak = () => {
|
||||||
return platform() === 'linux' && fs.existsSync('/.flatpak-info');
|
return platform() === 'linux' && fs.existsSync('/.flatpak-info');
|
||||||
}
|
};
|
||||||
|
|
||||||
// This function is used to get the python executable path
|
// This function is used to get the python executable path
|
||||||
// based on the platform. Use this for deployment.
|
// based on the platform. Use this for deployment.
|
||||||
const getPythonPath = () => {
|
export const getAppPaths = (basePath) => {
|
||||||
let pythonPath;
|
let pythonPath, pgadminFile;
|
||||||
switch (platform()) {
|
switch (platform()) {
|
||||||
case 'win32':
|
case 'win32':
|
||||||
pythonPath = '../python/python.exe';
|
pythonPath = '../../../../../python/python.exe';
|
||||||
|
pgadminFile = '../../../../../web/pgAdmin4.py';
|
||||||
break;
|
break;
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
pythonPath = '../../Frameworks/Python.framework/Versions/Current/bin/python3';
|
pythonPath = '../../../../Frameworks/Python.framework/Versions/Current/bin/python3';
|
||||||
|
pgadminFile = '../../../web/pgAdmin4.py';
|
||||||
break;
|
break;
|
||||||
case 'linux':
|
case 'linux':
|
||||||
pythonPath = '../venv/bin/python3';
|
pythonPath = '../../../../../venv/bin/python3';
|
||||||
|
pgadminFile = '../../../../../web/pgAdmin4.py';
|
||||||
if (insideFlatpak()) {
|
if (insideFlatpak()) {
|
||||||
pythonPath = '/usr/bin/python';
|
pythonPath = '/usr/bin/python';
|
||||||
|
pgadminFile = '/app/pgAdmin4/web/pgAdmin4.py';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
pgadminFile = '../../../web/pgAdmin4.py';
|
||||||
if (platform().startsWith('win')) {
|
if (platform().startsWith('win')) {
|
||||||
pythonPath = '../python/python.exe';
|
pythonPath = '../python/python.exe';
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,69 +57,15 @@ const getPythonPath = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pythonPath;
|
return [path.join(basePath, pythonPath), path.join(basePath, pgadminFile)];
|
||||||
};
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This function is used to get the [local] app data path
|
// This function is used to get the [local] app data path
|
||||||
// based on the platform. Use this for logs etc.
|
// based on the platform. Use this for logs etc.
|
||||||
const getLocalAppDataPath = () => {
|
const getLocalAppDataPath = () => {
|
||||||
let localAppDataPath;
|
let localAppDataPath = app.getPath('userData');
|
||||||
switch (platform()) {
|
if(process.platform == 'linux' && 'XDG_DATA_HOME' in process.env) {
|
||||||
case 'win32':
|
localAppDataPath = path.join(process.env.XDG_DATA_HOME, app.name);
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create directory if not exists
|
// Create directory if not exists
|
||||||
|
@ -128,14 +76,14 @@ const getLocalAppDataPath = () => {
|
||||||
|
|
||||||
// This function is used to get the random available TCP port
|
// 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.
|
// 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) {
|
return new Promise(function(resolve, reject) {
|
||||||
const server = net.createServer();
|
const server = net.createServer();
|
||||||
|
|
||||||
server.listen(fixedPort, '127.0.0.1');
|
server.listen(fixedPort, '127.0.0.1');
|
||||||
|
|
||||||
server.on('error', (e) => {
|
server.on('error', (e) => {
|
||||||
reject(e.code);
|
reject(e instanceof Error ? e : new Error(e.code));
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on('listening', () => {
|
server.on('listening', () => {
|
||||||
|
@ -147,13 +95,10 @@ const getAvailablePort = (fixedPort) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the app data folder path
|
// Get the app data folder path
|
||||||
const currentTime = (new Date()).getTime();
|
const serverLogFile = path.join(getLocalAppDataPath(), 'pgadmin4.' + (new Date()).getTime().toString() + '.log');
|
||||||
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};
|
|
||||||
|
|
||||||
// This function is used to read the file and return the content
|
// This function is used to read the file and return the content
|
||||||
const readServerLog = () => {
|
export const readServerLog = () => {
|
||||||
let data = null;
|
let data = null;
|
||||||
|
|
||||||
if (fs.existsSync(serverLogFile)) {
|
if (fs.existsSync(serverLogFile)) {
|
||||||
|
@ -168,7 +113,7 @@ const readServerLog = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// This function is used to write the data into the log file
|
// This function is used to write the data into the log file
|
||||||
const writeServerLog = (data) => {
|
export const writeServerLog = (data) => {
|
||||||
data = data + '\n';
|
data = data + '\n';
|
||||||
if (fs.existsSync(serverLogFile)) {
|
if (fs.existsSync(serverLogFile)) {
|
||||||
fs.writeFileSync(serverLogFile, data, {flag: 'a+'});
|
fs.writeFileSync(serverLogFile, data, {flag: 'a+'});
|
||||||
|
@ -185,28 +130,18 @@ const removeLogFile = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// This function used to set the object of pgAdmin server process.
|
// This function used to set the object of pgAdmin server process.
|
||||||
const setProcessObject = (processObject) => {
|
export const setProcessObject = (processObject) => {
|
||||||
pgadminServerProcess = 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.
|
// This function is used to get the server log file.
|
||||||
const getServerLogFile = () => {
|
export const getServerLogFile = () => {
|
||||||
return serverLogFile;
|
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
|
// This function is used to kill the server process, remove the log files
|
||||||
// and quit the application.
|
// and quit the application.
|
||||||
const cleanupAndQuitApp = () => {
|
export const cleanupAndQuitApp = (pgAdminWindowObject) => {
|
||||||
// Remove the server log file on exit
|
// Remove the server log file on exit
|
||||||
removeLogFile();
|
removeLogFile();
|
||||||
|
|
||||||
|
@ -216,247 +151,14 @@ const cleanupAndQuitApp = () => {
|
||||||
process.kill(pgadminServerProcess.pid);
|
process.kill(pgadminServerProcess.pid);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.warn('Failed to kill server process.');
|
console.warn('Failed to kill server process.', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pgAdminWindowObject != null) {
|
if (pgAdminWindowObject != null) {
|
||||||
// Remove all the cookies.
|
const ses = pgAdminWindowObject.webContents.session;
|
||||||
pgAdminWindowObject.cookies.getAll({}, function(cookies) {
|
ses.clearStorageData({
|
||||||
try {
|
storages: ['cookies'],
|
||||||
cookies.forEach(function(cookie) {
|
|
||||||
pgAdminWindowObject.cookies.remove({url: 'http://' + cookie.domain, name: cookie.name});
|
|
||||||
});
|
});
|
||||||
} 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
|
// This software is released under the PostgreSQL Licence
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
const axios = require('axios');
|
import { app, BrowserWindow, dialog, ipcMain, Menu, shell } from 'electron';
|
||||||
const fs = require('fs');
|
import axios from 'axios';
|
||||||
const path = require('path');
|
import Store from 'electron-store';
|
||||||
const misc = require('../js/misc.js');
|
import fs from 'fs';
|
||||||
const spawn = require('child_process').spawn;
|
import path from 'path';
|
||||||
const {EOL} = require('os');
|
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 pgadminServerProcess = null;
|
||||||
let startPageUrl = null;
|
let startPageUrl = null;
|
||||||
let serverCheckUrl = null;
|
let serverCheckUrl = null;
|
||||||
let addMenuCompleted = false;
|
|
||||||
let pgAdminMainScreen = null;
|
let pgAdminMainScreen = null;
|
||||||
|
|
||||||
let serverPort = 5050;
|
let serverPort = 5050;
|
||||||
let appStartTime = (new Date()).getTime();
|
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'];
|
let docsURLSubStrings = ['www.enterprisedb.com', 'www.postgresql.org', 'www.pgadmin.org', 'help/help'];
|
||||||
|
|
||||||
// Paths to the rest of the app
|
process.env['ELECTRON_ENABLE_SECURITY_WARNINGS'] = false;
|
||||||
let pythonPath = misc.getPythonPath();
|
|
||||||
let pgadminFile = '../web/pgAdmin4.py';
|
|
||||||
let configFile = '../web/config.py';
|
|
||||||
|
|
||||||
if (misc.insideFlatpak()) {
|
// Paths to the rest of the app
|
||||||
pgadminFile = '/app/pgAdmin4/web/pgAdmin4.py';
|
|
||||||
}
|
let [pythonPath, pgadminFile] = misc.getAppPaths(__dirname);
|
||||||
|
|
||||||
// Override the paths above, if a developer needs to
|
// Override the paths above, if a developer needs to
|
||||||
if (fs.existsSync('dev_config.json')) {
|
if (fs.existsSync('dev_config.json')) {
|
||||||
try {
|
try {
|
||||||
let dev_config = JSON.parse(fs.readFileSync('dev_config.json'));
|
let dev_config = JSON.parse(fs.readFileSync('dev_config.json'));
|
||||||
pythonPath = dev_config['pythonPath'];
|
pythonPath = path.resolve(dev_config['pythonPath']);
|
||||||
pgadminFile = dev_config['pgadminFile'];
|
pgadminFile = path.resolve(dev_config['pgadminFile']);
|
||||||
} catch (error) {
|
} 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
|
// This functions is used to start the pgAdmin4 server by spawning a
|
||||||
// separate process.
|
// separate process.
|
||||||
function startDesktopMode() {
|
function startDesktopMode() {
|
||||||
|
@ -52,6 +102,7 @@ function startDesktopMode() {
|
||||||
if (pgadminServerProcess != null)
|
if (pgadminServerProcess != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
let pingIntervalID;
|
||||||
let UUID = crypto.randomUUID();
|
let UUID = crypto.randomUUID();
|
||||||
// Set the environment variables so that pgAdmin 4 server
|
// Set the environment variables so that pgAdmin 4 server
|
||||||
// starts listening on the appropriate port.
|
// starts listening on the appropriate port.
|
||||||
|
@ -63,28 +114,25 @@ function startDesktopMode() {
|
||||||
startPageUrl = 'http://127.0.0.1:' + serverPort + '/?key=' + UUID;
|
startPageUrl = 'http://127.0.0.1:' + serverPort + '/?key=' + UUID;
|
||||||
serverCheckUrl = 'http://127.0.0.1:' + serverPort + '/misc/ping?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.
|
// Write Python Path, pgAdmin file path and command in log file.
|
||||||
misc.writeServerLog('pgAdmin Runtime Environment');
|
misc.writeServerLog('pgAdmin Runtime Environment');
|
||||||
misc.writeServerLog('--------------------------------------------------------');
|
misc.writeServerLog('--------------------------------------------------------');
|
||||||
let command = path.resolve(pythonPath) + ' -s ' + path.resolve(pgadminFile);
|
let command = pythonPath + ' -s ' + pgadminFile;
|
||||||
misc.writeServerLog('Python Path: "' + path.resolve(pythonPath) + '"');
|
misc.writeServerLog('Python Path: "' + pythonPath + '"');
|
||||||
misc.writeServerLog('Runtime Config File: "' + path.resolve(misc.getRunTimeConfigFile()) + '"');
|
misc.writeServerLog('Runtime Config File: "' + path.resolve(configStore.path) + '"');
|
||||||
misc.writeServerLog('pgAdmin Config File: "' + path.resolve(configFile) + '"');
|
misc.writeServerLog('Webapp Path: "' + pgadminFile + '"');
|
||||||
misc.writeServerLog('Webapp Path: "' + path.resolve(pgadminFile) + '"');
|
|
||||||
misc.writeServerLog('pgAdmin Command: "' + command + '"');
|
misc.writeServerLog('pgAdmin Command: "' + command + '"');
|
||||||
misc.writeServerLog('Environment: ');
|
misc.writeServerLog('Environment: ');
|
||||||
Object.keys(process.env).forEach(function (key) {
|
Object.keys(process.env).forEach(function (key) {
|
||||||
// Below code is included only for Mac OS as default path for azure CLI
|
// Below code is included only for Mac OS as default path for azure CLI
|
||||||
// installation path is not included in PATH variable while spawning
|
// installation path is not included in PATH variable while spawning
|
||||||
// runtime environment.
|
// runtime environment.
|
||||||
if (platform() === 'darwin' && key === 'PATH') {
|
if (process.platform === 'darwin' && key === 'PATH') {
|
||||||
let updated_path = process.env[key] + ':/usr/local/bin';
|
let updated_path = process.env[key] + ':/usr/local/bin';
|
||||||
process.env[key] = updated_path;
|
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');
|
let _libpq_path = path.join(path.dirname(path.dirname(path.resolve(pgadminFile))), 'runtime');
|
||||||
process.env[key] = _libpq_path + ';' + process.env[key];
|
process.env[key] = _libpq_path + ';' + process.env[key];
|
||||||
}
|
}
|
||||||
|
@ -95,12 +143,14 @@ function startDesktopMode() {
|
||||||
|
|
||||||
// Spawn the process to start pgAdmin4 server.
|
// Spawn the process to start pgAdmin4 server.
|
||||||
let spawnStartTime = (new Date).getTime();
|
let spawnStartTime = (new Date).getTime();
|
||||||
pgadminServerProcess = spawn(path.resolve(pythonPath), ['-s', path.resolve(pgadminFile)]);
|
pgadminServerProcess = spawn(pythonPath, ['-s', pgadminFile]);
|
||||||
pgadminServerProcess.on('error', function (err) {
|
pgadminServerProcess.on('error', function (err) {
|
||||||
// Log the error into the log file if process failed to launch
|
// Log the error into the log file if process failed to launch
|
||||||
misc.writeServerLog('Failed to launch pgAdmin4. Error:');
|
misc.writeServerLog('Failed to launch pgAdmin4. Error:');
|
||||||
misc.writeServerLog(err);
|
misc.writeServerLog(err);
|
||||||
|
showErrorDialog(pingIntervalID);
|
||||||
});
|
});
|
||||||
|
|
||||||
let spawnEndTime = (new Date).getTime();
|
let spawnEndTime = (new Date).getTime();
|
||||||
misc.writeServerLog('Total spawn time to start the pgAdmin4 server: ' + (spawnEndTime - spawnStartTime) / 1000 + ' Sec');
|
misc.writeServerLog('Total spawn time to start the pgAdmin4 server: ' + (spawnEndTime - spawnStartTime) / 1000 + ' Sec');
|
||||||
|
|
||||||
|
@ -120,7 +170,7 @@ function startDesktopMode() {
|
||||||
return axios.get(serverCheckUrl);
|
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 currentTime = (new Date).getTime();
|
||||||
let endTime = currentTime + connectionTimeout;
|
let endTime = currentTime + connectionTimeout;
|
||||||
let midTime1 = currentTime + (connectionTimeout / 2);
|
let midTime1 = currentTime + (connectionTimeout / 2);
|
||||||
|
@ -129,7 +179,7 @@ function startDesktopMode() {
|
||||||
|
|
||||||
// ping pgAdmin server every 1 second.
|
// ping pgAdmin server every 1 second.
|
||||||
let pingStartTime = (new Date).getTime();
|
let pingStartTime = (new Date).getTime();
|
||||||
let intervalID = setInterval(function () {
|
pingIntervalID = setInterval(function () {
|
||||||
// If ping request is already send and response is not
|
// If ping request is already send and response is not
|
||||||
// received no need to send another request.
|
// received no need to send another request.
|
||||||
if (pingInProgress)
|
if (pingInProgress)
|
||||||
|
@ -137,11 +187,11 @@ function startDesktopMode() {
|
||||||
|
|
||||||
pingServer().then(() => {
|
pingServer().then(() => {
|
||||||
pingInProgress = false;
|
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
|
// Set the pgAdmin process object to misc
|
||||||
misc.setProcessObject(pgadminServerProcess);
|
misc.setProcessObject(pgadminServerProcess);
|
||||||
|
|
||||||
clearInterval(intervalID);
|
clearInterval(pingIntervalID);
|
||||||
let appEndTime = (new Date).getTime();
|
let appEndTime = (new Date).getTime();
|
||||||
misc.writeServerLog('------------------------------------------');
|
misc.writeServerLog('------------------------------------------');
|
||||||
misc.writeServerLog('Total time taken to ping pgAdmin4 server: ' + (appEndTime - pingStartTime) / 1000 + ' Sec');
|
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
|
// if the connection timeout has lapsed then throw an error
|
||||||
// and stop pinging the server.
|
// and stop pinging the server.
|
||||||
if (curTime >= endTime) {
|
if (curTime >= endTime) {
|
||||||
clearInterval(intervalID);
|
showErrorDialog(pingIntervalID);
|
||||||
splashWindow.hide();
|
|
||||||
|
|
||||||
nw.Window.open('src/html/server_error.html', {
|
|
||||||
'frame': true,
|
|
||||||
'width': 790,
|
|
||||||
'height': 430,
|
|
||||||
'position': 'center',
|
|
||||||
'resizable': false,
|
|
||||||
'focus': true,
|
|
||||||
'show': true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curTime > midTime1) {
|
if (curTime > midTime1) {
|
||||||
if (curTime < midTime2) {
|
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 {
|
} 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;
|
pingInProgress = true;
|
||||||
}, 250);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is used to hide the splash screen and create/launch
|
// This function is used to hide the splash screen and create/launch
|
||||||
|
@ -189,7 +226,7 @@ function startDesktopMode() {
|
||||||
function launchPgAdminWindow() {
|
function launchPgAdminWindow() {
|
||||||
// Create and launch new window and open pgAdmin url
|
// Create and launch new window and open pgAdmin url
|
||||||
misc.writeServerLog('Application Server URL: ' + startPageUrl);
|
misc.writeServerLog('Application Server URL: ' + startPageUrl);
|
||||||
nw.Window.open(startPageUrl, {
|
pgAdminMainScreen = new BrowserWindow({
|
||||||
'id': 'pgadmin-main',
|
'id': 'pgadmin-main',
|
||||||
'icon': '../../assets/pgAdmin4.png',
|
'icon': '../../assets/pgAdmin4.png',
|
||||||
'frame': true,
|
'frame': true,
|
||||||
|
@ -201,158 +238,130 @@ function launchPgAdminWindow() {
|
||||||
'height': 768,
|
'height': 768,
|
||||||
'focus': true,
|
'focus': true,
|
||||||
'show': false,
|
'show': false,
|
||||||
}, (pgadminWindow) => {
|
webPreferences: {
|
||||||
pgAdminMainScreen = pgadminWindow;
|
nodeIntegrationInSubFrames: true,
|
||||||
// Set pgAdmin4 Windows Object
|
preload: path.join(__dirname, 'pgadmin_preload.js'),
|
||||||
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()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
pgadminWindow.on('closed', function () {
|
splashWindow.close();
|
||||||
misc.cleanupAndQuitApp();
|
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.
|
pgAdminMainScreen.loadURL(startPageUrl);
|
||||||
// Set the width and height for the new window.
|
pgAdminMainScreen.setBounds(configStore.get('bounds'));
|
||||||
pgadminWindow.on('new-win-policy', function (frame, url, policy) {
|
pgAdminMainScreen.show();
|
||||||
if (!frame) {
|
|
||||||
let openDocsInBrowser = misc.ConfigureStore.get('openDocsInBrowser', true);
|
pgAdminMainScreen.webContents.setWindowOpenHandler(({url})=>{
|
||||||
|
let openDocsInBrowser = configStore.get('openDocsInBrowser', true);
|
||||||
let isDocURL = false;
|
let isDocURL = false;
|
||||||
docsURLSubStrings.forEach(function (key) {
|
docsURLSubStrings.forEach(function (key) {
|
||||||
if (url.indexOf(key) >= 0) {
|
if (url.indexOf(key) >= 0) {
|
||||||
isDocURL = true;
|
isDocURL = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (openDocsInBrowser && isDocURL) {
|
if (openDocsInBrowser && isDocURL) {
|
||||||
// Do not open the window
|
// Do not open the window
|
||||||
policy.ignore();
|
shell.openExternal(url);
|
||||||
// Open URL in the external browser.
|
return { action: 'deny' };
|
||||||
nw.Shell.openExternal(url);
|
|
||||||
} else {
|
} else {
|
||||||
policy.setNewWindowManifest({
|
return {
|
||||||
'icon': '../../assets/pgAdmin4.png',
|
action: 'allow',
|
||||||
'frame': true,
|
overrideBrowserWindowOptions: {
|
||||||
'position': 'center',
|
'position': 'center',
|
||||||
'min_width': 640,
|
'min_width': 640,
|
||||||
'min_height': 480,
|
'min_height': 480,
|
||||||
'width': pgadminWindow.width,
|
icon: '../../assets/pgAdmin4.png',
|
||||||
'height': pgadminWindow.height,
|
...pgAdminMainScreen.getBounds(),
|
||||||
});
|
webPreferences: {
|
||||||
}
|
preload: path.join(__dirname, 'pgadmin_preload.js'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pgadminWindow.on('loaded', function () {
|
pgAdminMainScreen.on('close', () => {
|
||||||
/* Make the new window opener to null as it is
|
configStore.set('bounds', pgAdminMainScreen.getBounds());
|
||||||
* nothing but a splash screen. We will have to make it null,
|
misc.cleanupAndQuitApp(pgAdminMainScreen);
|
||||||
* so that open in new browser tab will work.
|
pgAdminMainScreen.removeAllListeners('close');
|
||||||
*/
|
pgAdminMainScreen.close();
|
||||||
pgadminWindow.window.hookConsole((method, args)=>{
|
|
||||||
misc.writeServerLog(
|
|
||||||
`--------------[UI ${method}]---------------${EOL}${misc.parseConsoleArgs(method, args)}${EOL}------------[UI End]----------------`);
|
|
||||||
});
|
});
|
||||||
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 {
|
try {
|
||||||
pgAdminMainScreen.isCustomMenusAdded = false;
|
await misc.getAvailablePort(fixedPort);
|
||||||
let addMenuInterval = setInterval(() => {
|
return true;
|
||||||
if (pgadminWindow?.window?.pgAdmin?.Browser?.Events && pgadminWindow?.window?.pgAdmin?.Browser?.MainMenus?.length > 0) {
|
} catch {
|
||||||
pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-enable-disable-menu-items', enableDisableMenuItem);
|
return false;
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
ipcMain.handle('openConfigure', openConfigure);
|
||||||
|
|
||||||
// Hide the splash screen
|
app.whenReady().then(() => {
|
||||||
splashWindow.hide();
|
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 () {
|
splashWindow.loadFile('./src/html/splash.html');
|
||||||
misc.unregisterZoomEvents();
|
splashWindow.center();
|
||||||
});
|
|
||||||
|
|
||||||
pgadminWindow.on('focus', function () {
|
splashWindow.on('show', function () {
|
||||||
misc.registerZoomEvents();
|
let fixedPortCheck = configStore.get('fixedPort', false);
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
if (fixedPortCheck) {
|
if (fixedPortCheck) {
|
||||||
serverPort = misc.ConfigureStore.get('portNo');
|
serverPort = configStore.get('portNo');
|
||||||
//Start the pgAdmin in Desktop mode.
|
//Start the pgAdmin in Desktop mode.
|
||||||
startDesktopMode();
|
startDesktopMode();
|
||||||
} else {
|
} else {
|
||||||
|
@ -365,339 +374,14 @@ splashWindow.on('loaded', function () {
|
||||||
})
|
})
|
||||||
.catch((errCode) => {
|
.catch((errCode) => {
|
||||||
if (errCode === 'EADDRINUSE') {
|
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 {
|
} else {
|
||||||
alert(errCode);
|
dialog.showErrorBox('Error', errCode.toString());
|
||||||
}
|
}
|
||||||
|
splashWindow.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
splashWindow.on('close', function () {
|
splashWindow.show();
|
||||||
misc.cleanupAndQuitApp();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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.constants import MIMETYPE_APP_JS
|
||||||
from pgadmin.utils.ajax import make_json_response
|
from pgadmin.utils.ajax import make_json_response
|
||||||
import config
|
import config
|
||||||
import httpagentparser
|
|
||||||
from pgadmin.model import User
|
from pgadmin.model import User
|
||||||
from user_agents import parse
|
from user_agents import parse
|
||||||
import platform
|
import platform
|
||||||
|
import re
|
||||||
|
|
||||||
MODULE_NAME = 'about'
|
MODULE_NAME = 'about'
|
||||||
|
|
||||||
|
@ -59,11 +59,11 @@ def index():
|
||||||
"""Render the about box."""
|
"""Render the about box."""
|
||||||
info = {}
|
info = {}
|
||||||
# Get OS , NW.js, Browser details
|
# 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)
|
admin = is_admin(current_user.email)
|
||||||
|
|
||||||
if nwjs_version:
|
if electron_version:
|
||||||
info['nwjs'] = nwjs_version
|
info['electron'] = electron_version
|
||||||
|
|
||||||
if config.SERVER_MODE:
|
if config.SERVER_MODE:
|
||||||
info['app_mode'] = gettext('Server')
|
info['app_mode'] = gettext('Server')
|
||||||
|
@ -119,32 +119,19 @@ def is_admin(load_user):
|
||||||
|
|
||||||
def detect_browser(request):
|
def detect_browser(request):
|
||||||
"""This function returns the browser and os details"""
|
"""This function returns the browser and os details"""
|
||||||
nwjs_version = None
|
electron_version = None
|
||||||
agent = request.environ.get('HTTP_USER_AGENT')
|
agent = request.environ.get('HTTP_USER_AGENT')
|
||||||
os_details = parse(platform.platform()).ua_string
|
os_details = parse(platform.platform()).ua_string
|
||||||
|
|
||||||
if 'Nwjs' in agent:
|
if 'Electron' in agent:
|
||||||
agent = agent.split('-')
|
electron_version = re.findall('Electron/([\\d.]+\\d+)', agent)[0]
|
||||||
nwjs_version = agent[0].split(':')[1]
|
|
||||||
browser = 'Chromium' + ' ' + agent[2]
|
|
||||||
|
|
||||||
else:
|
browser = re.findall(
|
||||||
browser = httpagentparser.detect(agent)
|
'(opera|chrome|safari|firefox|msie|trident(?=/))/?\\s*([\\d.]+\\d+)',
|
||||||
|
agent, re.IGNORECASE)
|
||||||
if not browser:
|
if not browser:
|
||||||
browser = agent.split('/')[0]
|
browser = agent.split('/')[0]
|
||||||
else:
|
else:
|
||||||
browser = browser['browser']['name'] + ' ' + browser['browser'][
|
browser = " ".join(browser[0])
|
||||||
'version']
|
|
||||||
|
|
||||||
return browser, os_details, nwjs_version
|
return browser, os_details, electron_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
|
|
||||||
)
|
|
||||||
|
|
|
@ -65,13 +65,13 @@ export default function AboutComponent() {
|
||||||
<InputLabel>{aboutData.current_user}</InputLabel>
|
<InputLabel>{aboutData.current_user}</InputLabel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
{ aboutData.nwjs &&
|
{ aboutData.electron &&
|
||||||
<Grid container spacing={0} style={{marginBottom: '8px'}}>
|
<Grid container spacing={0} style={{marginBottom: '8px'}}>
|
||||||
<Grid item lg={3} md={3} sm={3} xs={12}>
|
<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>
|
||||||
<Grid item lg={9} md={9} sm={9} xs={12}>
|
<Grid item lg={9} md={9} sm={9} xs={12}>
|
||||||
<InputLabel>{aboutData.nwjs}</InputLabel>
|
<InputLabel>{aboutData.electron}</InputLabel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ const MAIN_MENUS = [
|
||||||
];
|
];
|
||||||
|
|
||||||
let { name: browser } = getBrowser();
|
let { name: browser } = getBrowser();
|
||||||
if (browser == 'Nwjs') {
|
if (browser == 'Electron') {
|
||||||
let controlKey = isMac() ? 'cmd' : 'ctrl';
|
let controlKey = isMac() ? 'cmd' : 'ctrl';
|
||||||
let fullScreenKey = isMac() ? 'F' : 'F10';
|
let fullScreenKey = isMac() ? 'F' : 'F10';
|
||||||
|
|
||||||
|
@ -47,9 +47,31 @@ if (browser == 'Nwjs') {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default class MainMenuFactory {
|
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() {
|
static createMainMenus() {
|
||||||
pgAdmin.Browser.MainMenus = [];
|
pgAdmin.Browser.MainMenus = [];
|
||||||
MAIN_MENUS.forEach((_menu) => {
|
MAIN_MENUS.forEach((_menu) => {
|
||||||
|
@ -70,6 +92,12 @@ export default class MainMenuFactory {
|
||||||
});
|
});
|
||||||
|
|
||||||
pgAdmin.Browser.enable_disable_menus();
|
pgAdmin.Browser.enable_disable_menus();
|
||||||
|
|
||||||
|
window.electronUI?.onMenuClick((menuName)=>{
|
||||||
|
MainMenuFactory.electronCallbacks[menuName]?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.electronUI?.setMenus(MainMenuFactory.toElectron());
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSeparator(label, priority) {
|
static getSeparator(label, priority) {
|
||||||
|
@ -78,7 +106,8 @@ export default class MainMenuFactory {
|
||||||
|
|
||||||
static refreshMainMenuItems(menu, menuItems) {
|
static refreshMainMenuItems(menu, menuItems) {
|
||||||
menu.setMenuItems(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) {
|
static createMenuItem(options) {
|
||||||
|
@ -102,6 +131,7 @@ export default class MainMenuFactory {
|
||||||
}
|
}
|
||||||
}}, (menu, item)=> {
|
}}, (menu, item)=> {
|
||||||
pgAdmin.Browser.Events.trigger('pgadmin:nw-enable-disable-menu-items', menu, item);
|
pgAdmin.Browser.Events.trigger('pgadmin:nw-enable-disable-menu-items', menu, item);
|
||||||
|
window.electronUI?.enableDisableMenuItems(menu?.serialize(), item?.serialize());
|
||||||
}, (item) => {
|
}, (item) => {
|
||||||
pgAdmin.Browser.Events.trigger('pgadmin:nw-update-checked-menu-item', item);
|
pgAdmin.Browser.Events.trigger('pgadmin:nw-update-checked-menu-item', item);
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,17 +3,29 @@
|
||||||
{% block title %}{{ config.APP_NAME }}{% endblock %}
|
{% block title %}{{ config.APP_NAME }}{% endblock %}
|
||||||
|
|
||||||
{% block init_script %}
|
{% 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']) {
|
for (const method of ['log', 'error']) {
|
||||||
const nativeMethod = window.console[method];
|
const nativeMethod = window.console[method];
|
||||||
window.console[method] = function () {
|
window.console[method] = function () {
|
||||||
nativeMethod.apply(this, arguments);
|
nativeMethod.apply(this, arguments);
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
callback(method, arguments);
|
window.electronUI?.log(`--------------[UI ${method}]---------------
|
||||||
|
${parseConsoleArgs(arguments)}
|
||||||
|
------------[UI End]----------------`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
require(
|
require(
|
||||||
['sources/generated/app.bundle', 'sources/generated/browser_nodes'],
|
['sources/generated/app.bundle', 'sources/generated/browser_nodes'],
|
||||||
|
|
|
@ -85,30 +85,35 @@ export default class FileManagerModule {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
showNative(params, onOK, onCancel) {
|
async showNative(params, onOK, onCancel) {
|
||||||
// https://docs.nwjs.io/en/latest/References/Changes%20to%20DOM/
|
let res;
|
||||||
let fileEle = document.createElement('input');
|
let options = {};
|
||||||
let accept = params.supported_types?.map((v)=>(v=='*' ? '' : `.${v}`))?.join(',');
|
|
||||||
fileEle.setAttribute('type', 'file');
|
options['filters'] = params.supported_types?.map((v)=>(
|
||||||
fileEle.setAttribute('accept', accept);
|
v=='*' ? {name: 'All Files', extensions: ['*']} :
|
||||||
fileEle.onchange = (e)=>{
|
{name: `${v.toUpperCase()} File .${v}`, extensions:[v]}
|
||||||
if(e.target.value) {
|
));
|
||||||
onOK?.(e.target.value);
|
|
||||||
} else {
|
|
||||||
onCancel?.();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if(params.dialog_type == 'create_file') {
|
if(params.dialog_type == 'create_file') {
|
||||||
fileEle.setAttribute('nwsaveas', '');
|
res = await window.electronUI.showSaveDialog(options);
|
||||||
} else if(params.dialog_type == 'select_folder') {
|
} else {
|
||||||
fileEle.setAttribute('nwdirectory', '');
|
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) {
|
show(params, onOK, onCancel, modalObj) {
|
||||||
let {name: browser} = getBrowser();
|
let {name: browser} = getBrowser();
|
||||||
if(browser == 'Nwjs') {
|
if(browser == 'Electron') {
|
||||||
try {
|
try {
|
||||||
this.showNative(params, onOK, onCancel);
|
this.showNative(params, onOK, onCancel);
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
@ -111,8 +111,8 @@ export default function BrowserComponent({pgAdmin}) {
|
||||||
<PgAdminContext.Provider value={pgAdmin}>
|
<PgAdminContext.Provider value={pgAdmin}>
|
||||||
<ModalProvider>
|
<ModalProvider>
|
||||||
<NotifierProvider pgAdmin={pgAdmin} onReady={()=>setUiReady(true)}/>
|
<NotifierProvider pgAdmin={pgAdmin} onReady={()=>setUiReady(true)}/>
|
||||||
{browser != 'Nwjs' && <AppMenuBar />}
|
{browser != 'Electron' && <AppMenuBar />}
|
||||||
<div style={{height: (browser != 'Nwjs' ? 'calc(100% - 30px)' : '100%')}}>
|
<div style={{height: (browser != 'Electron' ? 'calc(100% - 30px)' : '100%')}}>
|
||||||
<Layout
|
<Layout
|
||||||
getLayoutInstance={(obj)=>{
|
getLayoutInstance={(obj)=>{
|
||||||
pgAdmin.Browser.docker = obj;
|
pgAdmin.Browser.docker = obj;
|
||||||
|
|
|
@ -24,6 +24,16 @@ export default class Menu {
|
||||||
return menuObj;
|
return menuObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serialize() {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
label: this.label,
|
||||||
|
name: this.name,
|
||||||
|
index: this.index,
|
||||||
|
addSepratior: this.addSepratior,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
addMenuItem(menuItem, index=null) {
|
addMenuItem(menuItem, index=null) {
|
||||||
if (menuItem instanceof MenuItem) {
|
if (menuItem instanceof MenuItem) {
|
||||||
menuItem.parentMenu = this;
|
menuItem.parentMenu = this;
|
||||||
|
@ -116,7 +126,6 @@ export class MenuItem {
|
||||||
url: '#',
|
url: '#',
|
||||||
target: '_self',
|
target: '_self',
|
||||||
enable: true,
|
enable: true,
|
||||||
type: 'normal'
|
|
||||||
};
|
};
|
||||||
_.extend(this, defaults, _.pick(options, menu_opts));
|
_.extend(this, defaults, _.pick(options, menu_opts));
|
||||||
if (!this.callback) {
|
if (!this.callback) {
|
||||||
|
@ -136,6 +145,17 @@ export class MenuItem {
|
||||||
return MenuItem(options);
|
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) {
|
change_checked(isChecked) {
|
||||||
this.checked = isChecked;
|
this.checked = isChecked;
|
||||||
this.changeChecked?.(this);
|
this.changeChecked?.(this);
|
||||||
|
|
|
@ -377,14 +377,17 @@ export function evalFunc(obj, func, ...param) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBrowser() {
|
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) || [];
|
let ua=navigator.userAgent,tem,M=(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i).exec(ua) || [];
|
||||||
if(/trident/i.test(M[1])) {
|
if(/trident/i.test(M[1])) {
|
||||||
tem=/\brv[ :]+(\d+)/g.exec(ua) || [];
|
tem=/\brv[ :]+(\d+)/g.exec(ua) || [];
|
||||||
return {name:'IE', version:(tem[1]||'')};
|
return {name:'IE', version:(tem[1]||'')};
|
||||||
}
|
}
|
||||||
if(ua.startsWith('Nwjs')) {
|
if(ua.indexOf('Electron') >= 0) {
|
||||||
let nwjs = ua.split('-')[0]?.split(':');
|
return {name: 'Electron', version: ua.match(/Electron\/([\d\.]+\d+)/)[1]};
|
||||||
return {name:nwjs[0], version: nwjs[1]};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(M[1]==='Chrome') {
|
if(M[1]==='Chrome') {
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { Box } from '@mui/material';
|
||||||
import { getDatabaseLabel, getTitle, setQueryToolDockerTitle } from '../sqleditor_title';
|
import { getDatabaseLabel, getTitle, setQueryToolDockerTitle } from '../sqleditor_title';
|
||||||
import gettext from 'sources/gettext';
|
import gettext from 'sources/gettext';
|
||||||
import NewConnectionDialog from './dialogs/NewConnectionDialog';
|
import NewConnectionDialog from './dialogs/NewConnectionDialog';
|
||||||
import { evalFunc } from '../../../../../static/js/utils';
|
import { evalFunc, getBrowser } from '../../../../../static/js/utils';
|
||||||
import { Notifications } from './sections/Notifications';
|
import { Notifications } from './sections/Notifications';
|
||||||
import MacrosDialog from './dialogs/MacrosDialog';
|
import MacrosDialog from './dialogs/MacrosDialog';
|
||||||
import FilterDialog from './dialogs/FilterDialog';
|
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(()=>{
|
useEffect(()=>{
|
||||||
getSQLScript();
|
getSQLScript();
|
||||||
initializeQueryTool();
|
initializeQueryTool();
|
||||||
|
@ -418,7 +424,14 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||||
});
|
});
|
||||||
|
|
||||||
eventBus.current.registerListener(QUERY_TOOL_EVENTS.FORCE_CLOSE_PANEL, ()=>{
|
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.close(qtPanelId, true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
qtPanelDocker.eventBus.registerListener(LAYOUT_EVENTS.CLOSING, (id)=>{
|
qtPanelDocker.eventBus.registerListener(LAYOUT_EVENTS.CLOSING, (id)=>{
|
||||||
|
@ -561,7 +574,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||||
const events = [
|
const events = [
|
||||||
[QUERY_TOOL_EVENTS.TRIGGER_LOAD_FILE, ()=>{
|
[QUERY_TOOL_EVENTS.TRIGGER_LOAD_FILE, ()=>{
|
||||||
let fileParams = {
|
let fileParams = {
|
||||||
'supported_types': ['*', 'sql'], // file types allowed
|
'supported_types': ['sql', '*'], // file types allowed
|
||||||
'dialog_type': 'select_file', // open select file dialog
|
'dialog_type': 'select_file', // open select file dialog
|
||||||
};
|
};
|
||||||
pgAdmin.Tools.FileManager.show(fileParams, (fileName, storage)=>{
|
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);
|
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.SAVE_FILE, qtState.current_file);
|
||||||
} else {
|
} else {
|
||||||
let fileParams = {
|
let fileParams = {
|
||||||
'supported_types': ['*', 'sql'],
|
'supported_types': ['sql', '*'],
|
||||||
'dialog_type': 'create_file',
|
'dialog_type': 'create_file',
|
||||||
'dialog_title': 'Save File',
|
'dialog_title': 'Save File',
|
||||||
'btn_primary': 'Save',
|
'btn_primary': 'Save',
|
||||||
|
@ -632,13 +645,18 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
// Add beforeunload event if "Confirm on close or refresh" option is enabled in the preferences.
|
// 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){
|
if(qtState.preferences.browser.confirm_on_refresh_close){
|
||||||
window.addEventListener('beforeunload', onBeforeUnload);
|
window.addEventListener('beforeunload', onBeforeUnload);
|
||||||
} else {
|
} else {
|
||||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
window.removeEventListener('beforeunload', onBeforeUnloadElectron);
|
||||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||||
};
|
};
|
||||||
}, [qtState.preferences.browser]);
|
}, [qtState.preferences.browser]);
|
||||||
|
|
Loading…
Reference in New Issue