Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cda7d7de90 | ||
|
|
e7981c7f11 | ||
|
|
ba87cbecfd | ||
|
|
bf6b963c45 | ||
|
|
9f8acfc51d | ||
|
|
8cf316b853 | ||
|
|
55817f685f | ||
|
|
35d585251a | ||
|
|
242dd48cbd | ||
|
|
30134800f3 | ||
|
|
e517fdf6bc | ||
|
|
b00ea3cd99 | ||
|
|
c0b6c8726e | ||
|
|
52c550e3fe | ||
|
|
57e72ce598 | ||
|
|
ba3deb5b9b | ||
|
|
fb0022923a | ||
|
|
ccaea82341 | ||
|
|
1a143c9e2c | ||
|
|
23d15bde8b | ||
|
|
49c55b41a2 | ||
|
|
4f822c8021 | ||
|
|
c3801165a5 | ||
|
|
fdf58f2d85 | ||
|
|
9a3b3530a6 | ||
|
|
b280c91013 | ||
|
|
95f6a63c70 | ||
|
|
96965797f6 | ||
|
|
7b5d96c5a6 | ||
|
|
00e2480b7b | ||
|
|
f98e3fa1fd | ||
|
|
81d8ea56f8 | ||
|
|
654dc0e2d7 | ||
|
|
b4e4b9d498 | ||
|
|
52af8d3e49 | ||
|
|
546806c40c | ||
|
|
ca7e93cc2c | ||
|
|
4ee76ba8c9 | ||
|
|
5d7ec14076 | ||
|
|
88f033e2a8 | ||
|
|
2e878a3fb2 | ||
|
|
c6e7ce03cc | ||
|
|
c45fb47b08 | ||
|
|
d39fe78239 | ||
|
|
0399d0be15 | ||
|
|
19237fe247 | ||
|
|
729c370125 | ||
|
|
636c224bd0 | ||
|
|
ab25238f17 | ||
|
|
b9ffbf73e0 | ||
|
|
23cad47f8d | ||
|
|
5fd308aafc | ||
|
|
88bee071e5 | ||
|
|
1fcb1f0228 | ||
|
|
9544ce1d18 | ||
|
|
8cf15ea17c | ||
|
|
bc10fa5c03 | ||
|
|
1d5f5a2067 | ||
|
|
70a7c42be1 | ||
|
|
d0e6090761 | ||
|
|
87ba177ff3 | ||
|
|
0511554db9 | ||
|
|
026292f07b | ||
|
|
18d507780d | ||
|
|
3aedae1436 | ||
|
|
9cbb9a3d24 | ||
|
|
b173fda116 | ||
|
|
d9ccef94a2 | ||
|
|
eff63931f2 | ||
|
|
3ec676194b | ||
|
|
8030bc708b | ||
|
|
fd78faf0cc | ||
|
|
bfcdcdc4f1 | ||
|
|
a75273a714 | ||
|
|
c9cc5257c7 | ||
|
|
3b4fa40253 | ||
|
|
9ee896aac4 | ||
|
|
01cb1839f8 | ||
|
|
1eb07608e9 | ||
|
|
8e16e000c3 | ||
|
|
64d7c2c62d | ||
|
|
e028ebc152 | ||
|
|
30051f3257 | ||
|
|
c4a52687b6 | ||
|
|
a06c13623d | ||
|
|
b303693ae8 | ||
|
|
6b012193e3 | ||
|
|
cd6ff48cc0 | ||
|
|
dbdea181a9 | ||
|
|
721a5da895 | ||
|
|
8578acd915 | ||
|
|
f3c95d9643 | ||
|
|
5c5b61c4db | ||
|
|
760e38293c | ||
|
|
dcfef154ce | ||
|
|
10b2e91e29 | ||
|
|
4f415f9768 | ||
|
|
93b5bc6bf8 | ||
|
|
60c2d0e23b | ||
|
|
c44710c9d7 | ||
|
|
37e034b3ef | ||
|
|
adb8fdb096 | ||
|
|
22cdb86aab | ||
|
|
576dc55615 | ||
|
|
f8fa1cf6d6 | ||
|
|
3bb9f0ba8c | ||
|
|
91eb60a363 | ||
|
|
4457a6a6a1 | ||
|
|
6f8cde67a6 | ||
|
|
391db5131d | ||
|
|
62a61b403e | ||
|
|
e8f108154d | ||
|
|
ccd84b7c2a | ||
|
|
522f3458f6 | ||
|
|
7efed95ac3 | ||
|
|
754cd223c1 | ||
|
|
a0b7dcca64 | ||
|
|
8876183981 | ||
|
|
feb3093c6d | ||
|
|
6f96f67655 | ||
|
|
3be2221948 | ||
|
|
7d0bbf30ec | ||
|
|
9933a9a9e5 | ||
|
|
227f047810 | ||
|
|
95ce9e976e | ||
|
|
4a645343a9 | ||
|
|
3d107ea618 | ||
|
|
19f5e41b09 | ||
|
|
f7eeefa3a9 | ||
|
|
a8f0f5bb4f | ||
|
|
44d40f0785 | ||
|
|
d0b2752fea | ||
|
|
8e0c080faf | ||
|
|
6410ae5f69 | ||
|
|
a60cbf00c8 | ||
|
|
3e7a220225 | ||
|
|
9026f46220 | ||
|
|
fedbe550db | ||
|
|
2b45a1f7d5 | ||
|
|
31a91409a0 | ||
|
|
9279393dcd | ||
|
|
20caa63508 | ||
|
|
6984e97f8d | ||
|
|
95ffd1b9a1 | ||
|
|
4c5639bee6 | ||
|
|
d4b72e2d89 | ||
|
|
0452828cc7 | ||
|
|
007c4840c2 | ||
|
|
2b04f3ca1d | ||
|
|
3706c3126a | ||
|
|
1ba9f005a7 | ||
|
|
c3d6f8887a | ||
|
|
439d6cb28f | ||
|
|
b0e7847962 | ||
|
|
a946c70869 | ||
|
|
5866da8194 | ||
|
|
ad34ee2699 | ||
|
|
e03d65d547 | ||
|
|
b5c8692f34 | ||
|
|
f96977733f | ||
|
|
33bd02d337 | ||
|
|
ab01dde2ba | ||
|
|
dea5335ce5 | ||
|
|
288fd7ed12 | ||
|
|
c98cea2ea9 | ||
|
|
056aff4742 | ||
|
|
6357672387 | ||
|
|
bdadc77235 | ||
|
|
0cb75bdcab | ||
|
|
bc3052ed1b | ||
|
|
df2f3460f0 | ||
|
|
bdf4f39b2b | ||
|
|
fff192eb95 | ||
|
|
c47390e643 | ||
|
|
fd944280f1 | ||
|
|
fa71b06874 | ||
|
|
591308fbe8 | ||
|
|
a03b513389 | ||
|
|
8857f0d179 | ||
|
|
2d2aa45ec5 | ||
|
|
ba5b66ff4f | ||
|
|
285e3afeee | ||
|
|
b3cf9ce185 | ||
|
|
18deb16e36 | ||
|
|
1a1266a5d6 | ||
|
|
7cdcaa0533 | ||
|
|
d5f6802e5f | ||
|
|
bc1a2a0b4d | ||
|
|
7cb1d74c05 | ||
|
|
02dbb20b41 | ||
|
|
6b9e3f5d1c | ||
|
|
5f4db94179 | ||
|
|
de712b0805 | ||
|
|
fdfd6c7792 | ||
|
|
c6739ebd6b | ||
|
|
f673e68677 | ||
|
|
85d4473af4 | ||
|
|
45cafd2cff | ||
|
|
d5ac441ab7 | ||
|
|
8e372f194c | ||
|
|
55ad67ac52 | ||
|
|
cc999ae5a5 | ||
|
|
f66bd4bcfb | ||
|
|
dfaea944f0 | ||
|
|
ff287f32c5 | ||
|
|
a46f99d360 | ||
|
|
d98ee76b8d | ||
|
|
29a526626f | ||
|
|
6509e45b6f | ||
|
|
2fb5b26adb | ||
|
|
24aefd2c7a | ||
|
|
b5b0538fe3 |
No files matched your search
@@ -1,4 +1,3 @@
|
||||
.git
|
||||
web/node_modules
|
||||
web/*.log
|
||||
web/regression
|
||||
|
||||
@@ -18,6 +18,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Build the container
|
||||
run: docker build .
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: false
|
||||
context: .
|
||||
@@ -9,6 +9,7 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
.vscode
|
||||
.sonarlint
|
||||
.yarn
|
||||
/*.diff
|
||||
/*.patch
|
||||
@@ -38,6 +39,7 @@ runtime/pgAdmin4.app/
|
||||
runtime/pgAdmin4.pro.user*
|
||||
runtime/pgAdmin4_resource.rc
|
||||
runtime/release/
|
||||
runtime/dev_config.json
|
||||
runtime/ui_BrowserWindow.h
|
||||
web/config_local.py
|
||||
web/pgadmin/misc/themes/pgadmin.themes.json
|
||||
@@ -48,6 +50,7 @@ node_modules/
|
||||
web/pgAdmin/static/js/generated
|
||||
web/pgadmin/static/js/generated
|
||||
web/yarn-error.log
|
||||
web/commit_hash
|
||||
/web/cacert.pem
|
||||
auditjs.html
|
||||
auditpy.txt
|
||||
|
||||
@@ -31,6 +31,7 @@ RUN apk add --no-cache \
|
||||
yarn \
|
||||
zlib-dev
|
||||
|
||||
COPY .git .git
|
||||
# Create the /pgadmin4 directory and copy the source into it. Explicitly
|
||||
# remove the node_modules directory as we'll recreate a clean version, as well
|
||||
# as various other files we don't want
|
||||
@@ -60,7 +61,8 @@ RUN export CPPFLAGS="-DPNG_ARM_NEON_OPT=0" && \
|
||||
webpack.* \
|
||||
jest.config.js \
|
||||
babel.* \
|
||||
./pgadmin/static/js/generated/.cache
|
||||
./pgadmin/static/js/generated/.cache \
|
||||
/pgadmin4/.git
|
||||
|
||||
#########################################################################
|
||||
# Next, create the base environment for Python
|
||||
@@ -70,7 +72,7 @@ FROM alpine:latest AS env-builder
|
||||
|
||||
# Install dependencies
|
||||
COPY requirements.txt /
|
||||
RUN apk add --no-cache \
|
||||
RUN apk add --no-cache \
|
||||
make \
|
||||
python3 \
|
||||
py3-pip && \
|
||||
@@ -187,7 +189,7 @@ COPY LICENSE /pgadmin4/LICENSE
|
||||
COPY DEPENDENCIES /pgadmin4/DEPENDENCIES
|
||||
|
||||
# Install runtime dependencies and configure everything in one RUN step
|
||||
RUN apk add \
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
py3-pip \
|
||||
postfix \
|
||||
@@ -199,7 +201,7 @@ RUN apk add \
|
||||
libedit \
|
||||
libldap \
|
||||
libcap && \
|
||||
/venv/bin/python3 -m pip install --no-cache-dir gunicorn==20.1.0 && \
|
||||
/venv/bin/python3 -m pip install --no-cache-dir gunicorn==22.0.0 && \
|
||||
find / -type d -name '__pycache__' -exec rm -rf {} + && \
|
||||
useradd -r -u 5050 -g root -s /sbin/nologin pgadmin && \
|
||||
mkdir -p /run/pgadmin /var/lib/pgadmin && \
|
||||
|
||||
@@ -217,6 +217,8 @@ REM Main build sequence Ends
|
||||
|
||||
ECHO Installing javascript dependencies...
|
||||
CD "%BUILDROOT%\web"
|
||||
CALL yarn set version berry || EXIT /B 1
|
||||
CALL yarn set version 3 || EXIT /B 1
|
||||
CALL yarn install || EXIT /B 1
|
||||
CALL npm rebuild || EXIT /B 1
|
||||
|
||||
@@ -260,45 +262,51 @@ REM Main build sequence Ends
|
||||
RD /Q /S "%BUILDROOT%\docs\en_US\html\_sources" 1> nul 2>&1
|
||||
|
||||
ECHO Staging runtime components...
|
||||
XCOPY /S /I /E /H /Y "%WD%\runtime\assets" "%BUILDROOT%\runtime\assets" > nul || EXIT /B 1
|
||||
XCOPY /S /I /E /H /Y "%WD%\runtime\src" "%BUILDROOT%\runtime\src" > nul || EXIT /B 1
|
||||
MKDIR "%BUILDROOT%\runtime\resources\app"
|
||||
XCOPY /S /I /E /H /Y "%WD%\runtime\assets" "%BUILDROOT%\runtime\resources\app\assets" > nul || EXIT /B 1
|
||||
XCOPY /S /I /E /H /Y "%WD%\runtime\src" "%BUILDROOT%\runtime\resources\app\src" > nul || EXIT /B 1
|
||||
|
||||
COPY "%WD%\runtime\package.json" "%BUILDROOT%\runtime\" > nul || EXIT /B 1
|
||||
CD "%BUILDROOT%\runtime\"
|
||||
CALL yarn install --production=true || EXIT /B 1
|
||||
COPY "%WD%\runtime\package.json" "%BUILDROOT%\runtime\resources\app\" > nul || EXIT /B 1
|
||||
COPY "%WD%\runtime\.yarnrc.yml" "%BUILDROOT%\runtime\resources\app\" > nul || EXIT /B 1
|
||||
|
||||
ECHO Downloading NWjs to %TMPDIR%...
|
||||
REM Get a fresh copy of nwjs.
|
||||
REM NOTE: The nw download servers seem to be very unreliable, so at the moment we're using wget which retries
|
||||
CD "%BUILDROOT%\runtime\resources\app\"
|
||||
|
||||
REM YARN
|
||||
REM CALL yarn --cwd "%TMPDIR%" add nw || EXIT /B
|
||||
REM YARN END
|
||||
CALL yarn set version berry || EXIT /B 1
|
||||
CALL yarn set version 3 || EXIT /B 1
|
||||
CALL yarn plugin import workspace-tools || EXIT /B 1
|
||||
CALL yarn workspaces focus --production || EXIT /B 1
|
||||
|
||||
ECHO Removing yarn cache...
|
||||
RD /Q /S "%BUILDROOT%\runtime\resources\app\.yarn" 1> nul 2>&1
|
||||
|
||||
ECHO Downloading Electron to %TMPDIR%...
|
||||
REM Get a fresh copy of electron.
|
||||
|
||||
REM WGET
|
||||
REM Comment out the below line as the latest version having some
|
||||
REM problem https://github.com/nwjs/nw.js/issues/7964, so for the time being
|
||||
REM hardcoded the version to 0.77.0
|
||||
REM FOR /f "tokens=2 delims='" %%i IN ('yarn info nw ^| findstr "latest: "') DO SET "NW_VERSION=%%i"
|
||||
REM :GET_NW
|
||||
REM wget https://dl.nwjs.io/v%NW_VERSION%/nwjs-v%NW_VERSION%-win-x64.zip -O "%TMPDIR%\nwjs-v%NW_VERSION%-win-x64.zip"
|
||||
REM IF %ERRORLEVEL% NEQ 0 GOTO GET_NW
|
||||
FOR /f "tokens=*" %%i IN ('npm info electron version') DO SET "ELECTRON_VERSION=%%i"
|
||||
:GET_NW
|
||||
wget https://github.com/electron/electron/releases/download/v%ELECTRON_VERSION%/electron-v%ELECTRON_VERSION%-win32-x64.zip -O "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64.zip"
|
||||
IF %ERRORLEVEL% NEQ 0 GOTO GET_NW
|
||||
|
||||
SET "NW_VERSION=0.77.0"
|
||||
wget https://dl.nwjs.io/v%NW_VERSION%/nwjs-v%NW_VERSION%-win-x64.zip -O "%TMPDIR%\nwjs-v%NW_VERSION%-win-x64.zip"
|
||||
|
||||
tar -C "%TMPDIR%" -xvf "%TMPDIR%\nwjs-v%NW_VERSION%-win-x64.zip" || EXIT /B 1
|
||||
MKDIR "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64" || EXIT /B 1
|
||||
tar -C "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64" -xvf "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64.zip" || EXIT /B 1
|
||||
REM WGET END
|
||||
|
||||
REM YARN
|
||||
REM XCOPY /S /I /E /H /Y "%TMPDIR%\node_modules\nw\nwjs\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
REM YARN END
|
||||
REM XCOPY
|
||||
XCOPY /S /I /E /H /Y "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
REM XCOPY END
|
||||
|
||||
REM WGET
|
||||
XCOPY /S /I /E /H /Y "%TMPDIR%\nwjs-v%NW_VERSION%-win-x64\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
REM WGET END
|
||||
MOVE "%BUILDROOT%\runtime\electron.exe" "%BUILDROOT%\runtime\pgAdmin4.exe"
|
||||
|
||||
ECHO Downloading rcedit.exe...
|
||||
wget https://github.com/electron/rcedit/releases/download/v2.0.0/rcedit-x64.exe -O "%TMPDIR%\rcedit-x64.exe"
|
||||
|
||||
ECHO Replacing executable icon, description, version...
|
||||
%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%""
|
||||
|
||||
MOVE "%BUILDROOT%\runtime\nw.exe" "%BUILDROOT%\runtime\pgAdmin4.exe"
|
||||
ECHO Attempting to sign the pgAdmin4.exe...
|
||||
CALL "%PGADMIN_SIGNTOOL_DIR%\signtool.exe" sign /tr http://timestamp.digicert.com "%BUILDROOT%\runtime\pgAdmin4.exe"
|
||||
IF %ERRORLEVEL% NEQ 0 (
|
||||
@@ -309,10 +317,6 @@ REM Main build sequence Ends
|
||||
PAUSE
|
||||
)
|
||||
|
||||
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 Staging PostgreSQL components...
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libpq.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libcrypto-*-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
@@ -366,6 +370,8 @@ REM Main build sequence Ends
|
||||
ECHO Generating SBOM...
|
||||
CALL syft "%BUILDROOT%" -o cyclonedx-json > "%BUILDROOT%\sbom.json"
|
||||
|
||||
EXIT /B 0
|
||||
|
||||
:SIGN_INSTALLER
|
||||
ECHO Attempting to sign the installer...
|
||||
CALL "%PGADMIN_SIGNTOOL_DIR%\signtool.exe" sign /tr http://timestamp.digicert.com "%DISTROOT%\%INSTALLERNAME%"
|
||||
|
||||
@@ -118,6 +118,7 @@ debian:
|
||||
|
||||
docker:
|
||||
echo $(APP_NAME)
|
||||
git checkout HEAD
|
||||
docker build --pull -t ${APP_NAME} -t $(APP_NAME):latest -t $(APP_NAME):$(APP_RELEASE) -t $(APP_NAME):$(APP_RELEASE).$(APP_REVISION) .
|
||||
|
||||
docs:
|
||||
|
||||
@@ -14,7 +14,7 @@ and ReactJS, HTML5 with CSS for the client side processing and UI.
|
||||
|
||||
Although developed using web technologies, pgAdmin 4 can be deployed either on
|
||||
a web server using a browser, or standalone on a workstation. The runtime/
|
||||
subdirectory contains an NWjs based runtime application intended to allow this,
|
||||
subdirectory contains an Electron based runtime application intended to allow this,
|
||||
which will execute the Python server and display the UI.
|
||||
|
||||
## Building the Runtime
|
||||
@@ -35,13 +35,7 @@ paths it would expect to find in the standard package for your platform.
|
||||
You can then execute the runtime by running something like:
|
||||
|
||||
```bash
|
||||
node_modules/nw/nwjs/nw .
|
||||
```
|
||||
|
||||
or on macOS:
|
||||
|
||||
```bash
|
||||
node_modules/nw/nwjs/nwjs.app/Contents/MacOS/nwjs .
|
||||
yarn run start
|
||||
```
|
||||
|
||||
# Configuring the Python Environment
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
The bulk of pgAdmin is a Python web application written using the Flask framework
|
||||
on the backend, and HTML5 with CSS3,ReactJS on the front end. A
|
||||
desktop runtime is also included for users that prefer a desktop application to
|
||||
a web application, which is written using NWjs (Node Webkit).
|
||||
a web application, which is written using Electron.
|
||||
|
||||
Runtime
|
||||
*******
|
||||
|
||||
The runtime is based on NWjs which integrates a browser and the Python server
|
||||
The runtime is based on Electron which integrates a browser and the Python server
|
||||
creating a standalone application. The source code can be found in the
|
||||
**/runtime** directory in the source tree.
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ See :ref:`config_py` for more information on configuration settings.
|
||||
Desktop Runtime Standalone Application
|
||||
======================================
|
||||
|
||||
The Desktop Runtime is based on `NWjs <https://nwjs.io/>`_ which integrates a
|
||||
The Desktop Runtime is based on `Electron <https://www.electronjs.org/>`_ which integrates a
|
||||
browser and the Python server creating a standalone application.
|
||||
|
||||
.. image:: images/runtime_standalone.png
|
||||
|
||||
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 78 KiB |
@@ -48,6 +48,8 @@ When using main browser window, the following keyboard shortcuts are available:
|
||||
+----------------------------+--------------------+------------------------------------+
|
||||
| Shift + Alt + ] | Shift + Option + ] | Tabbed panel forward |
|
||||
+----------------------------+--------------------+------------------------------------+
|
||||
| Shift + Alt + w | Shift + Ctrl + w | Close tab panel |
|
||||
+----------------------------+--------------------+------------------------------------+
|
||||
| Shift + Alt + l | Shift + Option + l | Tools main menu |
|
||||
+----------------------------+--------------------+------------------------------------+
|
||||
| Shift + Alt + v | Shift + Option + v | View data |
|
||||
|
||||
@@ -282,7 +282,8 @@ Expand the *Miscellaneous* node to specify miscellaneous display preferences.
|
||||
* Use the *Themes* drop-down listbox to select the theme for pgAdmin. You'll also get a preview just below the
|
||||
drop down. You can also submit your own themes,
|
||||
check `here <https://github.com/pgadmin-org/pgadmin4/blob/master/README.md>`_ how.
|
||||
Currently we support Standard, Dark and High Contrast theme.
|
||||
Currently we support Standard, Dark and High Contrast and System theme. Selecting System option will follow
|
||||
your computer's settings.
|
||||
|
||||
The Paths Node
|
||||
**************
|
||||
@@ -399,6 +400,9 @@ Use the fields on the *Editor* panel to change settings of the query editor.
|
||||
changed to text/plain. Keyword highlighting and code folding will be disabled.
|
||||
This will improve editor performance with large files.
|
||||
|
||||
* When the *Highlight selection matches?* switch is set to *True*, the editor will
|
||||
highlight matched selected text.
|
||||
|
||||
.. image:: images/preferences_sql_explain.png
|
||||
:alt: Preferences dialog sqleditor explain options
|
||||
:align: center
|
||||
|
||||
@@ -194,6 +194,8 @@ Data Editing Options
|
||||
| | | |
|
||||
| | * Click the *Paste with SERIAL/IDENTITY values?* if you want to paste the copied column values | |
|
||||
| | in the serial/identity columns. | |
|
||||
| | | |
|
||||
| | Note that copied row having *Bytea* datatype cell will be pasted as *Null*. | |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------+----------------+
|
||||
| *Delete* | Click the *Delete* icon to mark the selected rows for deletion. These marked rows get deleted | Option/Alt + |
|
||||
| | | Shift + D |
|
||||
|
||||
@@ -12,6 +12,10 @@ notes for it.
|
||||
:maxdepth: 1
|
||||
|
||||
|
||||
release_notes_8_11
|
||||
release_notes_8_10
|
||||
release_notes_8_9
|
||||
release_notes_8_8
|
||||
release_notes_8_7
|
||||
release_notes_8_6
|
||||
release_notes_8_5
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
************
|
||||
Version 8.10
|
||||
************
|
||||
|
||||
Release date: 2024-07-29
|
||||
|
||||
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v8.9.
|
||||
|
||||
Supported Database Servers
|
||||
**************************
|
||||
**PostgreSQL**: 12, 13, 14, 15, 16 and 17
|
||||
|
||||
**EDB Advanced Server**: 12, 13, 14, 15, and 16
|
||||
|
||||
Bundled PostgreSQL Utilities
|
||||
****************************
|
||||
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 16.3
|
||||
|
||||
|
||||
New features
|
||||
************
|
||||
|
||||
| `Issue #3981 <https://github.com/pgadmin-org/pgadmin4/issues/3981>`_ - Add support for Postgres Server Logs for Text, CSV and JSON format in plain and tabular formats. Upgrade React to version 18.
|
||||
| `Issue #6572 <https://github.com/pgadmin-org/pgadmin4/issues/6572>`_ - Add a keyboard shortcut to close active tab panel.
|
||||
| `Issue #7530 <https://github.com/pgadmin-org/pgadmin4/issues/7530>`_ - Add support for highlighting selection matches in the query editor.
|
||||
|
||||
Housekeeping
|
||||
************
|
||||
|
||||
| `Issue #7494 <https://github.com/pgadmin-org/pgadmin4/issues/7494>`_ - Replace pgAdmin NW.js container with Electron container.
|
||||
| `Issue #7501 <https://github.com/pgadmin-org/pgadmin4/issues/7501>`_ - Updated to the latest version of the Notistack library.
|
||||
| `Issue #7537 <https://github.com/pgadmin-org/pgadmin4/issues/7537>`_ - Ensure that pgAdmin 4 is compatible with PostgreSQL v17.
|
||||
| `Issue #7607 <https://github.com/pgadmin-org/pgadmin4/issues/7607>`_ - Automatically apply virtualization in the DataGridView of SchemaView if the schema contains only one collection.
|
||||
| `Issue #7623 <https://github.com/pgadmin-org/pgadmin4/issues/7623>`_ - Add the git commit hash details to the About dialog.
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Issue #3199 <https://github.com/pgadmin-org/pgadmin4/issues/3199>`_ - Fixed an issue where paste operation in query tool data grid should skip bytea columns and put the value as NULL instead.
|
||||
| `Issue #4165 <https://github.com/pgadmin-org/pgadmin4/issues/4165>`_ - Fixed an issue where the taskbar icon appeared as a red square for the query tool and schema diff when opened in a new window.
|
||||
| `Issue #5345 <https://github.com/pgadmin-org/pgadmin4/issues/5345>`_ - Fix issue with missing new added records in download file.
|
||||
| `Issue #5610 <https://github.com/pgadmin-org/pgadmin4/issues/5610>`_ - Fixed an issue where the File Open dialog did not show files without a dot extension.
|
||||
| `Issue #6548 <https://github.com/pgadmin-org/pgadmin4/issues/6548>`_ - Ensure pgAdmin never makes network requests to Google etc.
|
||||
| `Issue #6571 <https://github.com/pgadmin-org/pgadmin4/issues/6571>`_ - Fixed an issue where pop-up notifications from Object Explorer wouldn't get dismissed automatically if the Query Tool was opened.
|
||||
| `Issue #7035 <https://github.com/pgadmin-org/pgadmin4/issues/7035>`_ - Fixed the permission denied issue for functions of the pgstattuple extension when accessing statistics with a non-admin user.
|
||||
| `Issue #7219 <https://github.com/pgadmin-org/pgadmin4/issues/7219>`_ - Ensure processes related notifiers disappears.
|
||||
| `Issue #7297 <https://github.com/pgadmin-org/pgadmin4/issues/7297>`_ - Updated entrypoint.sh to utilize the email-validator package for email validation.
|
||||
| `Issue #7511 <https://github.com/pgadmin-org/pgadmin4/issues/7511>`_ - Fixed an issue where users could not insert characters at the desired location, as it was added to the end of the line.
|
||||
| `Issue #7554 <https://github.com/pgadmin-org/pgadmin4/issues/7554>`_ - Fixed an issue where sorting the database activity table on the dashboard by any column caused the details to expand in the wrong position.
|
||||
| `Issue #7618 <https://github.com/pgadmin-org/pgadmin4/issues/7618>`_ - Fix an issue where the preferences JSON file has no effect when an external database is used.
|
||||
| `Issue #7626 <https://github.com/pgadmin-org/pgadmin4/issues/7626>`_ - Fixed an issue where theme preview under theme options was broken in pgAdmin server mode.
|
||||
| `Issue #7627 <https://github.com/pgadmin-org/pgadmin4/issues/7627>`_ - Fixed an issue where users could not autofill their saved passwords in the connect server dialog in the browser.
|
||||
| `Issue #7638 <https://github.com/pgadmin-org/pgadmin4/issues/7638>`_ - Fixed an issue where Generate Script button should be disabled if no objects are selected in the schema diff result.
|
||||
| `Issue #7639 <https://github.com/pgadmin-org/pgadmin4/issues/7639>`_ - Fixed an issue where ERD Open/Save shorcuts were not working on Windows/Linux.
|
||||
| `Issue #7660 <https://github.com/pgadmin-org/pgadmin4/issues/7660>`_ - Add a precautionary check for the query tool connection cursor to fix the error 'NoneType' object has no attribute '_query'.
|
||||
| `Issue #7662 <https://github.com/pgadmin-org/pgadmin4/issues/7662>`_ - Fixed an issue where boolean values in node details of graphical explain plan were not interpreted correctly.
|
||||
| `Issue #7663 <https://github.com/pgadmin-org/pgadmin4/issues/7663>`_ - Fixed an issue where Reassign/Drop Owned dialog not opening for Role.
|
||||
| `Issue #7679 <https://github.com/pgadmin-org/pgadmin4/issues/7679>`_ - Ensure pgadmin does not try to connect to the server if saved password is not available.
|
||||
| `Issue #7681 <https://github.com/pgadmin-org/pgadmin4/issues/7681>`_ - Ensure that pgAdmin works when opened in an iframe.
|
||||
@@ -0,0 +1,45 @@
|
||||
************
|
||||
Version 8.11
|
||||
************
|
||||
|
||||
Release date: 2024-08-25
|
||||
|
||||
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v8.10.
|
||||
|
||||
Supported Database Servers
|
||||
**************************
|
||||
**PostgreSQL**: 12, 13, 14, 15, 16 and 17
|
||||
|
||||
**EDB Advanced Server**: 12, 13, 14, 15, and 16
|
||||
|
||||
Bundled PostgreSQL Utilities
|
||||
****************************
|
||||
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 16.3
|
||||
|
||||
|
||||
New features
|
||||
************
|
||||
|
||||
| `Issue #2046 <https://github.com/pgadmin-org/pgadmin4/issues/2046>`_ - Add a home button to the geometry viewer to set original zoom level when the viewer was opened.
|
||||
|
||||
Housekeeping
|
||||
************
|
||||
|
||||
| `Issue #7705 <https://github.com/pgadmin-org/pgadmin4/issues/7705>`_ - Upgrade react-data-grid fork to latest and change pgAdmin accordingly.
|
||||
| `Issue #7776 <https://github.com/pgadmin-org/pgadmin4/issues/7776>`_ - Introduce custom React Hook useSchemaState to simplify SchemaView component.
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Issue #7499 <https://github.com/pgadmin-org/pgadmin4/issues/7499>`_ - Fixed an issue where refreshing the Schema Diff tool opened in a new tab caused an error.
|
||||
| `Issue #7540 <https://github.com/pgadmin-org/pgadmin4/issues/7540>`_ - Fix server heartbeat logging error after deleting the server.
|
||||
| `Issue #7682 <https://github.com/pgadmin-org/pgadmin4/issues/7682>`_ - Fixed an issue where the Generate Script ignored filter conditions when a parent node was selected.
|
||||
| `Issue #7683 <https://github.com/pgadmin-org/pgadmin4/issues/7683>`_ - Fixed an issue where delete object(shortcut key) affecting both text and Object Explorer items.
|
||||
| `Issue #7688 <https://github.com/pgadmin-org/pgadmin4/issues/7688>`_ - Fix an issue where ERD tool should to be able to open saved pgerd file when using keyboard shortcuts.
|
||||
| `Issue #7728 <https://github.com/pgadmin-org/pgadmin4/issues/7728>`_ - Updated the documentation for web server authentication.
|
||||
| `Issue #7737 <https://github.com/pgadmin-org/pgadmin4/issues/7737>`_ - Fixed an issue where the REVOKE statement in the create script was throwing an error if the role contained special characters.
|
||||
| `Issue #7748 <https://github.com/pgadmin-org/pgadmin4/issues/7748>`_ - Improve code highlighting in query editor.
|
||||
| `Issue #7754 <https://github.com/pgadmin-org/pgadmin4/issues/7754>`_ - Fix an issue where the wheel package is not getting installed on the arm64-based macOS version < 14.
|
||||
| `Issue #7772 <https://github.com/pgadmin-org/pgadmin4/issues/7772>`_ - Fixed an issue where column resizing is not working in search objects dialog.
|
||||
| `Issue #7775 <https://github.com/pgadmin-org/pgadmin4/issues/7775>`_ - Fixed an issue where the value in the find box is not updating with selected text in editor if find is already open and re-triggered.
|
||||
| `Issue #7793 <https://github.com/pgadmin-org/pgadmin4/issues/7793>`_ - Fixed paths for Flatpak broken after Electron changes.
|
||||
@@ -0,0 +1,35 @@
|
||||
***********
|
||||
Version 8.8
|
||||
***********
|
||||
|
||||
Release date: 2024-06-06
|
||||
|
||||
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v8.7.
|
||||
|
||||
Supported Database Servers
|
||||
**************************
|
||||
**PostgreSQL**: 12, 13, 14, 15, and 16
|
||||
|
||||
**EDB Advanced Server**: 12, 13, 14, 15, and 16
|
||||
|
||||
Bundled PostgreSQL Utilities
|
||||
****************************
|
||||
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 16.3
|
||||
|
||||
|
||||
New features
|
||||
************
|
||||
|
||||
|
||||
Housekeeping
|
||||
************
|
||||
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Issue #5785 <https://github.com/pgadmin-org/pgadmin4/issues/5785>`_ - Fix an issue where user authentication fails with special characters in password.
|
||||
| `Issue #7480 <https://github.com/pgadmin-org/pgadmin4/issues/7480>`_ - Fixed an issue where canceling a query without privilege does not display any message on query tool.
|
||||
| `Issue #7520 <https://github.com/pgadmin-org/pgadmin4/issues/7520>`_ - Fix the issue where docker with SSL v8.7 fails to start.
|
||||
| `Issue #7524 <https://github.com/pgadmin-org/pgadmin4/issues/7524>`_ - Fixed an issue where the size displayed as 'NaN B' for all databases in the statistics tab.
|
||||
| `Issue #7526 <https://github.com/pgadmin-org/pgadmin4/issues/7526>`_ - Fixed an issue where backup and restore operations failed with the error 'This build does not support compression with gzip'.
|
||||
@@ -0,0 +1,50 @@
|
||||
***********
|
||||
Version 8.9
|
||||
***********
|
||||
|
||||
Release date: 2024-06-27
|
||||
|
||||
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v8.8.
|
||||
|
||||
Supported Database Servers
|
||||
**************************
|
||||
**PostgreSQL**: 12, 13, 14, 15, and 16
|
||||
|
||||
**EDB Advanced Server**: 12, 13, 14, 15, and 16
|
||||
|
||||
Bundled PostgreSQL Utilities
|
||||
****************************
|
||||
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 16.3
|
||||
|
||||
|
||||
New features
|
||||
************
|
||||
|
||||
| `Issue #5932 <https://github.com/pgadmin-org/pgadmin4/issues/5932>`_ - Provide option to set theme based on OS theme preference.
|
||||
|
||||
Housekeeping
|
||||
************
|
||||
|
||||
| `Issue #7363 <https://github.com/pgadmin-org/pgadmin4/issues/7363>`_ - Remove the usage of MUI makeStyles as it doesn't support React 18.
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Issue #6357 <https://github.com/pgadmin-org/pgadmin4/issues/6357>`_ - Disable the query tool editor input if any SQL is being loaded to prevent users from typing.
|
||||
| `Issue #7241 <https://github.com/pgadmin-org/pgadmin4/issues/7241>`_ - Fixed an issue where resizable data editors in query tool should not be allowed to resize beyond the app window bounds.
|
||||
| `Issue #7295 <https://github.com/pgadmin-org/pgadmin4/issues/7295>`_ - Fixed new line indentation in query editor and add a user preference to disable it.
|
||||
| `Issue #7306 <https://github.com/pgadmin-org/pgadmin4/issues/7306>`_ - Ensure that a user can connect to a server using SSL certificates and identity files from a shared storage.
|
||||
| `Issue #7316 <https://github.com/pgadmin-org/pgadmin4/issues/7316>`_ - Fixed an issue where object explorer toolbar button not showing shortcut in the tooltip.
|
||||
| `Issue #7414 <https://github.com/pgadmin-org/pgadmin4/issues/7414>`_ - Add support for comments on RLS policy object.
|
||||
| `Issue #7476 <https://github.com/pgadmin-org/pgadmin4/issues/7476>`_ - Fixed an issue where changing a column name should reflect in all the constraints in table object dialog and ERD table dialog.
|
||||
| `Issue #7481 <https://github.com/pgadmin-org/pgadmin4/issues/7481>`_ - Fixed an issue where dark theme shows white background when all tabs are closed.
|
||||
| `Issue #7516 <https://github.com/pgadmin-org/pgadmin4/issues/7516>`_ - Ensure preferences can be loaded using preferences.json.
|
||||
| `Issue #7523 <https://github.com/pgadmin-org/pgadmin4/issues/7523>`_ - Fixed an issue where the schema diff incorrectly marked tables as different due to schema-qualified trigger functions in the trigger definitions.
|
||||
| `Issue #7528 <https://github.com/pgadmin-org/pgadmin4/issues/7528>`_ - Fixed an issue where backslash breaks syntax highlighting.
|
||||
| `Issue #7536 <https://github.com/pgadmin-org/pgadmin4/issues/7536>`_ - Search Objects dialog should focus on search input on open.
|
||||
| `Issue #7542 <https://github.com/pgadmin-org/pgadmin4/issues/7542>`_ - Fixed incorrect sorting of size in statistics tab.
|
||||
| `Issue #7555 <https://github.com/pgadmin-org/pgadmin4/issues/7555>`_ - Fixed an issue where query tool shortcuts for find/replace are not working.
|
||||
| `Issue #7556 <https://github.com/pgadmin-org/pgadmin4/issues/7556>`_ - Fixed migration failure while using external database.
|
||||
| `Issue #7593 <https://github.com/pgadmin-org/pgadmin4/issues/7593>`_ - Fixed an issue where query tool auto-complete stops working after connection change.
|
||||
| `Issue #7605 <https://github.com/pgadmin-org/pgadmin4/issues/7605>`_ - Fix the permissions issue in the pgAdmin installation directory on Debian and RHEL-8 platforms (CVE-2024-6238).
|
||||
| `Issue #7616 <https://github.com/pgadmin-org/pgadmin4/issues/7616>`_ - Fixed an issue where delete button in Users data was enabled for incorrect rows.
|
||||
@@ -21,6 +21,7 @@ Use the fields in the *General* tab to define the RLS Policy:
|
||||
* Use the *Name* field to add a descriptive name for the RLS Policy. The name will be displayed in the *pgAdmin* tree control.
|
||||
* Use the drop-down listbox next to *Role* to select the Role to which the RLS Policy is to be applied.
|
||||
* Use the drop-down listbox next to *Type* to select the type of the policy.
|
||||
* Store notes about the policy in the Comment field.
|
||||
|
||||
Click the *Commands* tab to continue.
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ display information about the object currently selected in the *pgAdmin* tree
|
||||
control in the left window. Select a tab to access information about the
|
||||
highlighted object in the tree control.
|
||||
|
||||
.. image:: images/main_dashboard_general.png
|
||||
:alt: Dashboard panel
|
||||
.. image:: images/dashboard_activity.png
|
||||
:alt: Dashboard Activity
|
||||
:align: center
|
||||
|
||||
The graphs and tables on the *Dashboard* tab provides an active analysis of system statistics and the usage
|
||||
statistics for the selected server or database.
|
||||
|
||||
Click the *General* tab to get the usage statistics for the selected server or database:
|
||||
Click the *Activity* tab to get the usage statistics for the selected server or database:
|
||||
|
||||
* The *Server sessions* or *Database sessions* graph displays the interactions
|
||||
with the server or database.
|
||||
@@ -30,8 +30,14 @@ Click the *General* tab to get the usage statistics for the selected server or d
|
||||
or fetched from the buffer cache (but not the operating system's file system
|
||||
cache) for the server or database.
|
||||
|
||||
The *Server activity* panel displays information about sessions, locks, prepared
|
||||
transactions, and server configuration (if applicable). The information is
|
||||
.. image:: images/dashboard_stat.png
|
||||
:alt: Dashboard Activity
|
||||
:align: center
|
||||
|
||||
Click the *Stat* tab to get the usage statistics for the selected server or database:
|
||||
|
||||
The *Stat* panel displays information about sessions, locks, prepared
|
||||
transactions. The information is
|
||||
presented in context-sensitive tables. Use controls located above the table to:
|
||||
|
||||
* Click the *Refresh* button to update the information displayed in each table.
|
||||
@@ -56,6 +62,23 @@ session:
|
||||
* Use the *Details* icon (located in the third column) to open the *Details*
|
||||
tab; the tab displays information about the selected session.
|
||||
|
||||
.. image:: images/dashboard_config.png
|
||||
:alt: Dashboard Activity
|
||||
:align: center
|
||||
|
||||
Click the *Configuration* tab to get the server configuration details.
|
||||
|
||||
|
||||
.. image:: images/dashboard_logs.png
|
||||
:alt: Dashboard Activity
|
||||
:align: center
|
||||
|
||||
Click the *Logs* tab to get the server logs.
|
||||
|
||||
* Use the Log Format switch to select the format you want. Text/Plain, JSON and CSV are supported.
|
||||
The enable/disable state of the log format depends on the Postgresql.conf setting.
|
||||
* Use the Logs in tabular format? switch if you want to see the logs in a tabular format.
|
||||
|
||||
Click the *System Statistics* tab to get the statistics for the system:
|
||||
|
||||
.. image:: images/main_dashboard_sys_statistics_summary.png
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
{% trans copyright=copyright|e %}<li class="left" style="margin-left: 10px">© Copyright {{ copyright }}.</li>{% endtrans %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
<li class="right" style="margin-right: 10px"><a href="genindex.html" title="General Index" accesskey="I">index</a></li>
|
||||
<li class="right" style="margin-right: 10px"><a href="genindex.html" title="General Index">index</a></li>
|
||||
{%- if next %}
|
||||
<li class="right" ><a href="{{ next.link|e }}" title="{{ next.title|striptags|e }}" accesskey="N">next</a> |</li>
|
||||
<li class="right" ><a href="{{ next.link|e }}" title="{{ next.title|striptags|e }}">next</a> |</li>
|
||||
{%- endif %}
|
||||
{%- if prev %}
|
||||
<li class="right" ><a href="{{ prev.link|e }}" title="{{ prev.title|striptags|e }}" accesskey="P">previous</a> |</li>
|
||||
<li class="right" ><a href="{{ prev.link|e }}" title="{{ prev.title|striptags|e }}">previous</a> |</li>
|
||||
{%- endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -37,6 +37,7 @@ and modify the values for the following parameters:
|
||||
The default value is REMOTE_USER and the possible values are REMOTE_USER,
|
||||
HTTP_X_FORWARDED_USER, X-Forwarded-User."
|
||||
|
||||
.. note:: If REMOTE_USER does not work, try replacing it with either REMOTE-USER or Remote-user.
|
||||
|
||||
Master Password
|
||||
===============
|
||||
|
||||
@@ -21,7 +21,7 @@ _setup_dirs
|
||||
_create_python_virtualenv "debian"
|
||||
_build_runtime
|
||||
_build_docs "debian"
|
||||
_copy_code
|
||||
_copy_code "debian"
|
||||
_generate_sbom
|
||||
|
||||
#
|
||||
@@ -57,6 +57,7 @@ Description: The core server package for pgAdmin. pgAdmin is the most popular an
|
||||
EOF
|
||||
|
||||
# Build the Debian package for the server
|
||||
chmod -R u+rwX,go+rX,go-w "${SERVERROOT}"
|
||||
fakeroot dpkg-deb --build "${SERVERROOT}" "${DISTROOT}/${APP_NAME}-server_${APP_LONG_VERSION}_${OS_ARCH}.deb"
|
||||
|
||||
#
|
||||
@@ -67,6 +68,27 @@ fakeroot dpkg-deb --build "${SERVERROOT}" "${DISTROOT}/${APP_NAME}-server_${APP_
|
||||
echo "Creating the desktop package..."
|
||||
mkdir "${DESKTOPROOT}/DEBIAN"
|
||||
|
||||
# Ubuntu 24 requires apparmor profile to work.
|
||||
OS_ID=$(grep "^ID=" /etc/os-release | awk -F "=" '{ print $2 }')
|
||||
OS_VERSION=$(grep "^VERSION_ID=" /etc/os-release | awk -F "=" '{ print $2 }' | sed 's/"//g' | awk -F "." '{ print $1 }')
|
||||
|
||||
if [ "${OS_ID}" == 'ubuntu' ] && [ "${OS_VERSION}" -ge "24" ]; then
|
||||
cat << EOF > "${DESKTOPROOT}/DEBIAN/conffiles"
|
||||
/etc/apparmor.d/pgadmin4
|
||||
EOF
|
||||
|
||||
mkdir -p "${DESKTOPROOT}/etc/apparmor.d"
|
||||
cp "${SOURCEDIR}/pkg/debian/pgadmin4-aa-profile" "${DESKTOPROOT}/etc/apparmor.d/pgadmin4"
|
||||
|
||||
cat << EOF > "${DESKTOPROOT}/DEBIAN/postinst"
|
||||
#!/bin/sh
|
||||
|
||||
echo "Load apparmor pgAdmin profile..."
|
||||
apparmor_parser -r /etc/apparmor.d/pgadmin4
|
||||
EOF
|
||||
chmod 755 "${DESKTOPROOT}/DEBIAN/postinst"
|
||||
fi
|
||||
|
||||
cat << EOF > "${DESKTOPROOT}/DEBIAN/control"
|
||||
Package: ${APP_NAME}-desktop
|
||||
Version: ${APP_LONG_VERSION}
|
||||
@@ -79,6 +101,7 @@ Description: The desktop user interface for pgAdmin. pgAdmin is the most popular
|
||||
EOF
|
||||
|
||||
# Build the Debian package for the desktop
|
||||
chmod -R u+rwX,go+rX,go-w "${DESKTOPROOT}"
|
||||
fakeroot dpkg-deb --build "${DESKTOPROOT}" "${DISTROOT}/${APP_NAME}-desktop_${APP_LONG_VERSION}_${OS_ARCH}.deb"
|
||||
|
||||
#
|
||||
@@ -108,6 +131,7 @@ mkdir -p "${WEBROOT}/etc/apache2/conf-available"
|
||||
cp "${SOURCEDIR}/pkg/debian/pgadmin4.conf" "${WEBROOT}/etc/apache2/conf-available"
|
||||
|
||||
# Build the Debian package for the web
|
||||
chmod -R u+rwX,go+rX,go-w "${WEBROOT}"
|
||||
fakeroot dpkg-deb --build "${WEBROOT}" "${DISTROOT}/${APP_NAME}-web_${APP_LONG_VERSION}_all.deb"
|
||||
|
||||
#
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# This profile allows everything and only exists to give the
|
||||
# application a name instead of having the label "unconfined"
|
||||
|
||||
abi <abi/4.0>,
|
||||
include <tunables/global>
|
||||
|
||||
profile pgadmin4 /usr/pgadmin4/bin/pgadmin4 flags=(unconfined) {
|
||||
userns,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/pgadmin4>
|
||||
}
|
||||
@@ -43,10 +43,19 @@ if [ ! -f /var/lib/pgadmin/pgadmin4.db ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! echo "${PGADMIN_DEFAULT_EMAIL}" | grep -E "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$" > /dev/null; then
|
||||
# Validate PGADMIN_DEFAULT_EMAIL
|
||||
CHECK_EMAIL_DELIVERABILITY="False"
|
||||
if [ -n "${PGADMIN_CONFIG_CHECK_EMAIL_DELIVERABILITY}" ]; then
|
||||
CHECK_EMAIL_DELIVERABILITY=${PGADMIN_CONFIG_CHECK_EMAIL_DELIVERABILITY}
|
||||
fi
|
||||
|
||||
is_valid_email=$(cd /pgadmin4/pgadmin/utils && /venv/bin/python3 -c "from validation_utils import validate_email; val = validate_email('${PGADMIN_DEFAULT_EMAIL}', ${CHECK_EMAIL_DELIVERABILITY}); print(val)")
|
||||
if echo "${is_valid_email}" | grep "False" > /dev/null; then
|
||||
echo "'${PGADMIN_DEFAULT_EMAIL}' does not appear to be a valid email address. Please reset the PGADMIN_DEFAULT_EMAIL environment variable and try again."
|
||||
exit 1
|
||||
fi
|
||||
# Switch back to root directory for further process
|
||||
cd /pgadmin4
|
||||
|
||||
# Read secret contents
|
||||
if [ -n "${PGADMIN_DEFAULT_PASSWORD_FILE}" ]; then
|
||||
|
||||
@@ -133,55 +133,54 @@ _create_python_virtualenv() {
|
||||
_build_runtime() {
|
||||
echo "Assembling the desktop runtime..."
|
||||
|
||||
# Get a fresh copy of nwjs.
|
||||
# NOTE: The nw download servers seem to be very unreliable, so at the moment we're using wget
|
||||
# in a retry loop as Yarn/Npm don't seem to like that.
|
||||
# Get a fresh copy of electron.
|
||||
|
||||
# YARN:
|
||||
# yarn add --cwd "${BUILDROOT}" nw
|
||||
# YARN END
|
||||
ELECTRON_ARCH="x64"
|
||||
if [ "$(uname -m)" == "arm64" ]; then
|
||||
ELECTRON_ARCH="arm64"
|
||||
fi
|
||||
|
||||
# WGET:
|
||||
# Comment out the below line as the latest version having some
|
||||
# problem https://github.com/nwjs/nw.js/issues/7964, so for the time being
|
||||
# hardcoded the version to 0.77.0
|
||||
# NW_VERSION=$(yarn info nw | grep latest | awk -F "'" '{ print $2}')
|
||||
NW_VERSION="0.77.0"
|
||||
ELECTRON_VERSION="$(npm info electron version)"
|
||||
|
||||
pushd "${BUILDROOT}" > /dev/null || exit
|
||||
while true;do
|
||||
wget "https://dl.nwjs.io/v${NW_VERSION}/nwjs-v${NW_VERSION}-linux-x64.tar.gz" && break
|
||||
rm "nwjs-v${NW_VERSION}-linux-x64.tar.gz"
|
||||
wget "https://github.com/electron/electron/releases/download/v${ELECTRON_VERSION}/electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}.zip" && break
|
||||
rm "electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}.zip"
|
||||
done
|
||||
tar -zxvf "nwjs-v${NW_VERSION}-linux-x64.tar.gz"
|
||||
unzip "electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}.zip" -d "electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}"
|
||||
popd > /dev/null || exit
|
||||
# WGET END
|
||||
|
||||
# Copy nwjs into the staging directory
|
||||
# Copy electron into the staging directory
|
||||
mkdir -p "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
|
||||
# The chmod command below is needed to fix the permission issue of
|
||||
# the NWjs binaries and files.
|
||||
# Change the permission for others and group the same as the owner
|
||||
chmod -R og=u "${BUILDROOT}/nwjs-v${NW_VERSION}-linux-x64"/*
|
||||
chmod -R og=u "${BUILDROOT}/electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}"/*
|
||||
# Explicitly remove write permissions for others and group
|
||||
chmod -R og-w "${BUILDROOT}/nwjs-v${NW_VERSION}-linux-x64"/*
|
||||
chmod -R og-w "${BUILDROOT}/electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}"/*
|
||||
|
||||
# YARN:
|
||||
# cp -r "${BUILDROOT}/node_modules/nw/nwjs"/* "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
# YARN END
|
||||
BUNDLEDIR="${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
|
||||
# WGET:
|
||||
cp -r "${BUILDROOT}/nwjs-v${NW_VERSION}-linux-x64"/* "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
cp -r "${BUILDROOT}/electron-v${ELECTRON_VERSION}-linux-${ELECTRON_ARCH}"/* "${BUNDLEDIR}"
|
||||
# WGET END
|
||||
|
||||
mv "${DESKTOPROOT}/usr/${APP_NAME}/bin/nw" "${DESKTOPROOT}/usr/${APP_NAME}/bin/${APP_NAME}"
|
||||
mv "${BUNDLEDIR}/electron" "${BUNDLEDIR}/${APP_NAME}"
|
||||
|
||||
cp -r "${SOURCEDIR}/runtime/assets" "${DESKTOPROOT}/usr/${APP_NAME}/bin/assets"
|
||||
cp -r "${SOURCEDIR}/runtime/src" "${DESKTOPROOT}/usr/${APP_NAME}/bin/src"
|
||||
mkdir -p "${BUNDLEDIR}/resources/app"
|
||||
cp -r "${SOURCEDIR}/runtime/assets" "${BUNDLEDIR}/resources/app/assets"
|
||||
cp -r "${SOURCEDIR}/runtime/src" "${BUNDLEDIR}/resources/app/src"
|
||||
|
||||
cp "${SOURCEDIR}/runtime/package.json" "${DESKTOPROOT}/usr/${APP_NAME}/bin/"
|
||||
yarn --cwd "${DESKTOPROOT}/usr/${APP_NAME}/bin" install --production=true
|
||||
cp "${SOURCEDIR}/runtime/package.json" "${BUNDLEDIR}/resources/app"
|
||||
cp "${SOURCEDIR}/runtime/.yarnrc.yml" "${BUNDLEDIR}/resources/app"
|
||||
|
||||
# Install the runtime node_modules
|
||||
pushd "${BUNDLEDIR}/resources/app" > /dev/null || exit
|
||||
yarn set version berry
|
||||
yarn set version 3
|
||||
yarn plugin import workspace-tools
|
||||
yarn workspaces focus --production
|
||||
|
||||
popd > /dev/null || exit
|
||||
|
||||
# Create the icon
|
||||
mkdir -p "${DESKTOPROOT}/usr/share/icons/hicolor/128x128/apps/"
|
||||
@@ -212,10 +211,18 @@ _build_docs() {
|
||||
_copy_code() {
|
||||
echo "Copying the server code..."
|
||||
|
||||
OS_VERSION=$(grep "^VERSION_ID=" /etc/os-release | awk -F "=" '{ print $2 }' | sed 's/"//g' | awk -F "." '{ print $1 }')
|
||||
if [ "$1" == 'redhat' ] && [ "${OS_VERSION}" == "8" ]; then
|
||||
echo "Setting file permission through umask..."
|
||||
umask u+rwx,go+rx,go-w
|
||||
fi
|
||||
|
||||
# Remove any TCL-related files that may cause us problems
|
||||
find "${SERVERROOT}/usr/${APP_NAME}/venv/" -name "_tkinter*" -print0 | xargs -0 rm -rf
|
||||
|
||||
pushd "${SOURCEDIR}/web" > /dev/null || exit
|
||||
yarn set version berry
|
||||
yarn set version 3
|
||||
yarn install
|
||||
yarn run bundle
|
||||
popd > /dev/null || exit
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>%APPID%</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>23A334</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx14.0</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1501</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>15A507</string>
|
||||
<key>LSEnvironment</key>
|
||||
<dict>
|
||||
<key>MallocNanoZone</key>
|
||||
<string>0</string>
|
||||
</dict>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>%APPVER%</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -26,56 +26,37 @@ _build_runtime() {
|
||||
echo "Assembling the runtime environment..."
|
||||
|
||||
test -d "${BUILD_ROOT}" || mkdir "${BUILD_ROOT}"
|
||||
|
||||
# Get a fresh copy of nwjs.
|
||||
# NOTE: The nw download servers seem to be very unreliable, so at the moment we're using wget
|
||||
# in a retry loop as Yarn/Npm don't seem to like that.
|
||||
|
||||
# Get a fresh copy of electron
|
||||
# uname -m returns "x86_64" on Intel, but we need "x64"
|
||||
NW_ARCH="x64"
|
||||
ELECTRON_ARCH="x64"
|
||||
if [ "$(uname -m)" == "arm64" ]; then
|
||||
NW_ARCH="arm64"
|
||||
ELECTRON_ARCH="arm64"
|
||||
fi
|
||||
|
||||
# YARN:
|
||||
# yarn add --cwd "${BUILDROOT}" nw
|
||||
# YARN END
|
||||
|
||||
# WGET:
|
||||
# Comment out the below line as the latest version having some
|
||||
# problem https://github.com/nwjs/nw.js/issues/7964, so for the time being
|
||||
# hardcoded the version to 0.77.0
|
||||
# NW_VERSION=$(yarn info nw | grep latest | awk -F "'" '{ print $2}')
|
||||
NW_VERSION="0.77.0"
|
||||
ELECTRON_VERSION="$(npm info electron version)"
|
||||
|
||||
pushd "${BUILD_ROOT}" > /dev/null || exit
|
||||
while true;do
|
||||
wget "https://dl.nwjs.io/v${NW_VERSION}/nwjs-v${NW_VERSION}-osx-${NW_ARCH}.zip" && break
|
||||
rm "nwjs-v${NW_VERSION}-osx-${NW_ARCH}.zip"
|
||||
wget "https://github.com/electron/electron/releases/download/v${ELECTRON_VERSION}/electron-v${ELECTRON_VERSION}-darwin-${ELECTRON_ARCH}.zip" && break
|
||||
rm "electron-v${ELECTRON_VERSION}-darwin-${ELECTRON_ARCH}.zip"
|
||||
done
|
||||
unzip "nwjs-v${NW_VERSION}-osx-${NW_ARCH}.zip"
|
||||
unzip "electron-v${ELECTRON_VERSION}-darwin-${ELECTRON_ARCH}.zip"
|
||||
popd > /dev/null || exit
|
||||
# WGET END
|
||||
|
||||
# YARN:
|
||||
# cp -R "${BUILD_ROOT}/node_modules/nw/nwjs/nwjs.app" "${BUILD_ROOT}/"
|
||||
# YARN END
|
||||
|
||||
# WGET:
|
||||
cp -R "${BUILD_ROOT}/nwjs-v${NW_VERSION}-osx-${NW_ARCH}"/nwjs.app "${BUILD_ROOT}/"
|
||||
# WGET END
|
||||
|
||||
mv "${BUILD_ROOT}/nwjs.app" "${BUNDLE_DIR}"
|
||||
mv "${BUILD_ROOT}/Electron.app" "${BUNDLE_DIR}"
|
||||
find "${BUNDLE_DIR}" -exec touch {} \;
|
||||
|
||||
# Copy in the runtime code
|
||||
mkdir "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
cp -R "${SOURCE_DIR}/runtime/assets" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
cp -R "${SOURCE_DIR}/runtime/src" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
cp "${SOURCE_DIR}/runtime/package.json" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
cp "${SOURCE_DIR}/runtime/.yarnrc.yml" "${BUNDLE_DIR}/Contents/Resources/app.nw/"
|
||||
mkdir "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
cp -R "${SOURCE_DIR}/runtime/assets" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
cp -R "${SOURCE_DIR}/runtime/src" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
cp "${SOURCE_DIR}/runtime/package.json" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
cp "${SOURCE_DIR}/runtime/.yarnrc.yml" "${BUNDLE_DIR}/Contents/Resources/app/"
|
||||
|
||||
# Install the runtime node_modules, then replace the package.json
|
||||
pushd "${BUNDLE_DIR}/Contents/Resources/app.nw/" > /dev/null || exit
|
||||
pushd "${BUNDLE_DIR}/Contents/Resources/app/" > /dev/null || exit
|
||||
yarn set version berry
|
||||
yarn set version 3
|
||||
yarn plugin import workspace-tools
|
||||
@@ -154,6 +135,7 @@ _build_docs() {
|
||||
source "${BUILD_ROOT}/venv/bin/activate"
|
||||
pip3 install --upgrade pip
|
||||
pip3 install -r "${SOURCE_DIR}/requirements.txt"
|
||||
# Due to issue https://github.com/sphinx-doc/sphinx/issues/11739, we have pinned the Sphinx version to 6.1.3.
|
||||
pip3 install sphinx==6.1.3
|
||||
pip3 install sphinxcontrib-youtube
|
||||
|
||||
@@ -178,7 +160,6 @@ _fixup_imports() {
|
||||
# Find all the files that may need tweaks
|
||||
TODO=$(find . -perm +0111 -type f -exec file "{}" \; | \
|
||||
grep -v "Frameworks/Python.framework" | \
|
||||
grep -v "Frameworks/nwjs" | \
|
||||
grep -E "Mach-O 64-bit" | \
|
||||
awk -F ':| ' '{ORS=" "; print $1}' | \
|
||||
uniq)
|
||||
@@ -273,10 +254,19 @@ _complete_bundle() {
|
||||
sed -i '' "s/%APPNAME%/${APP_NAME}/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
||||
sed -i '' "s/%APPVER%/${APP_LONG_VERSION}/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
||||
sed -i '' "s/%APPID%/org.pgadmin.pgadmin4/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
||||
for FILE in "${BUNDLE_DIR}"/Contents/Resources/*.lproj/InfoPlist.strings; do
|
||||
sed -i '' 's/CFBundleGetInfoString =.*/CFBundleGetInfoString = "Copyright (C) 2013 - 2024, The pgAdmin Development Team";/g' "${FILE}"
|
||||
sed -i '' 's/NSHumanReadableCopyright =.*/NSHumanReadableCopyright = "Copyright (C) 2013 - 2024, The pgAdmin Development Team";/g' "${FILE}"
|
||||
echo CFBundleDisplayName = \""${APP_NAME}"\"\; >> "${FILE}"
|
||||
|
||||
# Rename helper execs and Update the plist
|
||||
for helper_exec in "Electron Helper" "Electron Helper (Renderer)" "Electron Helper (Plugin)" "Electron Helper (GPU)"
|
||||
do
|
||||
pgadmin_exec=${helper_exec//Electron/pgAdmin 4}
|
||||
mv "${BUNDLE_DIR}/Contents/Frameworks/${helper_exec}.app/Contents/MacOS/${helper_exec}" "${BUNDLE_DIR}/Contents/Frameworks/${helper_exec}.app/Contents/MacOS/${pgadmin_exec}"
|
||||
mv "${BUNDLE_DIR}/Contents/Frameworks/${helper_exec}.app" "${BUNDLE_DIR}/Contents/Frameworks/${pgadmin_exec}.app"
|
||||
|
||||
info_plist="${BUNDLE_DIR}/Contents/Frameworks/${pgadmin_exec}.app/Contents/Info.plist"
|
||||
cp Info.plist-helper.in "${info_plist}"
|
||||
sed -i '' "s/%APPNAME%/${pgadmin_exec}/g" "${info_plist}"
|
||||
sed -i '' "s/%APPVER%/${APP_LONG_VERSION}/g" "${info_plist}"
|
||||
sed -i '' "s/%APPID%/org.pgadmin.pgadmin4.helper/g" "${info_plist}"
|
||||
done
|
||||
|
||||
# PkgInfo
|
||||
@@ -286,10 +276,10 @@ _complete_bundle() {
|
||||
cp pgAdmin4.icns "${BUNDLE_DIR}/Contents/Resources/app.icns"
|
||||
|
||||
# Rename the executable
|
||||
mv "${BUNDLE_DIR}/Contents/MacOS/nwjs" "${BUNDLE_DIR}/Contents/MacOS/${APP_NAME}"
|
||||
mv "${BUNDLE_DIR}/Contents/MacOS/Electron" "${BUNDLE_DIR}/Contents/MacOS/${APP_NAME}"
|
||||
|
||||
# Rename the app in package.json so the menu looks as it should
|
||||
sed -i '' "s/\"name\": \"pgadmin4\"/\"name\": \"${APP_NAME}\"/g" "${BUNDLE_DIR}/Contents/Resources/app.nw/package.json"
|
||||
sed -i '' "s/\"name\": \"pgadmin4\"/\"name\": \"${APP_NAME}\"/g" "${BUNDLE_DIR}/Contents/Resources/app/package.json"
|
||||
|
||||
# Import the dependencies, and rewrite any library references
|
||||
_fixup_imports "${BUNDLE_DIR}"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<dict>
|
||||
<!--
|
||||
Disable Sandboxing. This must be enabled for the app store, but it will
|
||||
cause NWjs to fail to start with an error like:
|
||||
cause Electron to fail to start with an error like:
|
||||
|
||||
[1004/170922.238911:ERROR:directory_reader_posix.cc(42)] opendir /dev/fd: Operation not permitted (1)
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<string>%TEAMID%.org.pgadmin.pgadmin4</string>
|
||||
|
||||
<!--
|
||||
We have no need for JIT on x86_64, but NWJS won't start without it
|
||||
We have no need for JIT on x86_64, but Electron won't start without it
|
||||
on Apple Silicon.
|
||||
-->
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
<!--
|
||||
We need to enable this, even though we don't modify our own executables.
|
||||
Otherwise, NWjs just bombs out.
|
||||
Otherwise, Electron just bombs out.
|
||||
-->
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
|
||||
@@ -48,7 +48,7 @@ mkdir pip-build/pgadmin4/docs
|
||||
|
||||
# Build the clean tree
|
||||
cd web || exit
|
||||
for FILE in $(git ls-files)
|
||||
for FILE in $(git ls-files|grep -v regression)
|
||||
do
|
||||
echo Adding "${FILE}"
|
||||
# We use tar here to preserve the path, as Mac (for example) doesn't support cp --parents
|
||||
@@ -56,9 +56,15 @@ do
|
||||
tar cf - "${FILE}" | (cd ../pip-build/pgadmin4; tar xf -)
|
||||
done
|
||||
|
||||
yarn set version berry
|
||||
yarn set version 3
|
||||
yarn install
|
||||
yarn run bundle
|
||||
|
||||
# Copy the commit_hash file, it doesn't show up in git ls-files
|
||||
echo Adding "commit_hash"
|
||||
tar cf - "commit_hash" | (cd ../pip-build/pgadmin4; tar xf -)
|
||||
|
||||
for FILE in pgadmin/static/js/generated/*
|
||||
do
|
||||
echo Adding "${FILE}"
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import builtins
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
from codecs import open
|
||||
from importlib.machinery import SourceFileLoader
|
||||
|
||||
@@ -40,7 +41,17 @@ kerberos_extras = []
|
||||
# gssapi in it's own list
|
||||
for index, req in enumerate(all_requires):
|
||||
if 'psycopg[c]' in req:
|
||||
req = req.replace('psycopg[c]', 'psycopg[binary]')
|
||||
# Starting from Psycopg 3.1.20, ARM64 macOS binary packages are no
|
||||
# longer available for macOS versions before 14.0.
|
||||
_req = req.replace('psycopg[c]', 'psycopg[binary]')
|
||||
req = "psycopg[binary] == 3.1.19; sys_platform == 'darwin' and" \
|
||||
" platform_machine == 'arm64' and platform_release < '23.0' \n"\
|
||||
+ _req + "; (sys_platform == 'darwin' and" \
|
||||
" platform_machine == 'arm64' and" \
|
||||
" platform_release >= '23.0') or" \
|
||||
" (sys_platform == 'darwin' and" \
|
||||
" platform_machine != 'arm64'" \
|
||||
") or sys_platform != 'darwin'"
|
||||
|
||||
if 'gssapi' in req:
|
||||
kerberos_extras.append(req)
|
||||
|
||||
@@ -25,7 +25,7 @@ _setup_dirs
|
||||
_create_python_virtualenv "redhat"
|
||||
_build_runtime
|
||||
_build_docs "redhat"
|
||||
_copy_code
|
||||
_copy_code "redhat"
|
||||
_generate_sbom
|
||||
|
||||
# Get an RPM-compatible version number
|
||||
@@ -80,6 +80,8 @@ fi
|
||||
cp -rfa %{pga_build_root}/server/* \${RPM_BUILD_ROOT}
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,755)
|
||||
%dir /usr/pgadmin4
|
||||
/usr/pgadmin4/*
|
||||
EOF
|
||||
|
||||
@@ -121,6 +123,8 @@ cp -rfa %{pga_build_root}/desktop/* \${RPM_BUILD_ROOT}
|
||||
/bin/xdg-icon-resource forceupdate
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,755)
|
||||
%attr(755,root,root) /usr/pgadmin4/bin/pgadmin4
|
||||
/usr/pgadmin4/bin/*
|
||||
/usr/share/icons/hicolor/128x128/apps/*
|
||||
/usr/share/icons/hicolor/64x64/apps/*
|
||||
@@ -131,7 +135,9 @@ cp -rfa %{pga_build_root}/desktop/* \${RPM_BUILD_ROOT}
|
||||
/usr/pgadmin4/sbom-desktop.json
|
||||
EOF
|
||||
|
||||
# Build the Redhat package for the server
|
||||
|
||||
|
||||
# Build the Redhat package for the desktop
|
||||
rpmbuild --define "pga_build_root ${BUILDROOT}" -bb "${BUILDROOT}/desktop.spec"
|
||||
|
||||
|
||||
@@ -168,6 +174,7 @@ The web interface for pgAdmin, hosted under Apache HTTPD. pgAdmin is the most po
|
||||
cp -rfa %{pga_build_root}/web/* \${RPM_BUILD_ROOT}
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,755)
|
||||
/usr/pgadmin4/bin/*
|
||||
%config(noreplace) /etc/httpd/conf.d/*
|
||||
/usr/pgadmin4/sbom-web.json
|
||||
|
||||
@@ -39,14 +39,17 @@ yum groupinstall -y "Development Tools"
|
||||
|
||||
if [ "${OS_VERSION}" == "8" ]; then
|
||||
yum install -y expect fakeroot postgresql15-devel python3.9-devel nodejs yarn rpm-build rpm-sign yum-utils krb5-devel
|
||||
# Due to issue https://github.com/sphinx-doc/sphinx/issues/11739, we have pinned the Sphinx version to 6.1.3.
|
||||
pip3.9 install sphinx==6.1.3
|
||||
pip3.9 install sphinxcontrib-youtube
|
||||
elif [ "${OS_VERSION}" == "9" ]; then
|
||||
yum install -y expect libpq5-devel postgresql15-devel python3-devel nodejs yarn rpm-build rpm-sign yum-utils krb5-devel
|
||||
# Due to issue https://github.com/sphinx-doc/sphinx/issues/11739, we have pinned the Sphinx version to 6.1.3.
|
||||
pip3 install sphinx==6.1.3
|
||||
pip3 install sphinxcontrib-youtube
|
||||
else
|
||||
yum install -y expect fakeroot postgresql15-devel python3-devel nodejs yarn rpm-build rpm-sign yum-utils krb5-devel
|
||||
# Due to issue https://github.com/sphinx-doc/sphinx/issues/11739, we have pinned the Sphinx version to 6.1.3.
|
||||
pip3 install sphinx==6.1.3
|
||||
pip3 install sphinxcontrib-youtube
|
||||
fi
|
||||
|
||||
@@ -32,6 +32,9 @@ fi
|
||||
TARBALL_NAME=$(echo "${APP_NAME}-${APP_LONG_VERSION}" | sed 's/ //g' | awk '{print tolower($0)}')
|
||||
DOC_TARBALL_NAME=$(echo "${APP_NAME}-${APP_LONG_VERSION}-docs" | sed 's/ //g' | awk '{print tolower($0)}')
|
||||
|
||||
# Get the github timestamp
|
||||
git log -1 --format='%H %as' > web/commit_hash
|
||||
|
||||
# Output basic details to show we're working
|
||||
echo "Building tarballs for ${APP_NAME} version ${APP_LONG_VERSION}..."
|
||||
|
||||
@@ -69,6 +72,10 @@ do
|
||||
tar cf - "${FILE}" | (cd "src-build/${TARBALL_NAME}"; tar xf -)
|
||||
done
|
||||
|
||||
# Copy the commit_hash file, it doesn't show up in git ls-files
|
||||
echo Adding "web/commit_hash"
|
||||
tar cf - "web/commit_hash" | (cd "src-build/${TARBALL_NAME}"; tar xf -)
|
||||
|
||||
# Create the tarball
|
||||
echo Creating tarball...
|
||||
cd src-build || exit
|
||||
|
||||
@@ -8,59 +8,50 @@
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
Flask==3.0.*; python_version > '3.7'
|
||||
Flask==2.2.*; python_version <= '3.7'
|
||||
Flask==3.0.*
|
||||
Flask-Login==0.*
|
||||
Flask-Mail==0.*
|
||||
Flask-Migrate==4.*
|
||||
greenlet==1.1.2; python_version <= '3.10'
|
||||
Flask-SQLAlchemy==3.1.*; python_version > '3.7'
|
||||
Flask-SQLAlchemy==3.0.*; python_version <= '3.7'
|
||||
Flask-WTF==1.2.*; python_version > '3.7'
|
||||
Flask-WTF==1.1.1; python_version <= '3.7'
|
||||
Flask-SQLAlchemy==3.1.*
|
||||
Flask-WTF==1.2.*
|
||||
Flask-Compress==1.*
|
||||
Flask-Paranoid==0.*
|
||||
Flask-Babel==4.0.*; python_version > '3.7'
|
||||
Flask-Babel==3.1.*; python_version <= '3.7'
|
||||
Flask-Security-Too==5.4.*; python_version > '3.7'
|
||||
Flask-Security-Too==5.1.*; python_version <= '3.7'
|
||||
Flask-Babel==4.0.*
|
||||
Flask-Security-Too==5.5.*; python_version >= '3.10'
|
||||
Flask-Security-Too==5.4.*; python_version <= '3.9'
|
||||
Flask-SocketIO==5.3.*
|
||||
WTForms==3.1.*; python_version > '3.7'
|
||||
WTForms==3.0.*; python_version <= '3.7'
|
||||
WTForms==3.1.*
|
||||
passlib==1.*
|
||||
pytz==2024.*
|
||||
speaklater3==1.*
|
||||
sqlparse==0.*
|
||||
psutil==5.9.*
|
||||
psycopg[c]==3.1.12
|
||||
psutil==6.0.*
|
||||
psycopg[c]==3.2.1
|
||||
python-dateutil==2.*
|
||||
SQLAlchemy==2.*
|
||||
bcrypt==4.0.*
|
||||
cryptography==42.0.*
|
||||
bcrypt==4.2.*
|
||||
cryptography==43.0.*
|
||||
sshtunnel==0.*
|
||||
ldap3==2.*
|
||||
gssapi==1.8.*
|
||||
eventlet==0.34.2
|
||||
httpagentparser==1.9.*
|
||||
eventlet==0.36.1
|
||||
user-agents==2.2.0
|
||||
pywinpty==2.0.*; sys_platform=="win32"
|
||||
Authlib==1.3.*; python_version > '3.7'
|
||||
Authlib==1.2.*; python_version <= '3.7'
|
||||
Authlib==1.3.*
|
||||
pyotp==2.*
|
||||
qrcode==7.*
|
||||
boto3==1.33.*
|
||||
boto3==1.34.*
|
||||
urllib3==1.26.*
|
||||
azure-mgmt-rdbms==10.1.0
|
||||
azure-mgmt-resource==23.0.1
|
||||
azure-mgmt-resource==23.1.1
|
||||
azure-mgmt-subscription==3.1.1
|
||||
azure-identity==1.15.0
|
||||
azure-identity==1.17.1
|
||||
google-api-python-client==2.*
|
||||
google-auth-oauthlib==1.2.0
|
||||
keyring==24.*; python_version > '3.7'
|
||||
keyring==23.*; python_version <= '3.7'
|
||||
Werkzeug==3.0.*; python_version > '3.7'
|
||||
Werkzeug==2.2.3; python_version <= '3.7'
|
||||
google-auth-oauthlib==1.2.1
|
||||
keyring==25.*
|
||||
Werkzeug==3.0.*
|
||||
typer[all]==0.12.*
|
||||
setuptools==69.*; python_version >= '3.12'
|
||||
setuptools==72.*; python_version >= '3.12'
|
||||
jsonformatter~=0.3.2
|
||||
libgravatar==1.0.*
|
||||
@@ -1,6 +0,0 @@
|
||||
generated
|
||||
node_modules
|
||||
vendor
|
||||
templates/
|
||||
templates\
|
||||
ycache
|
||||
@@ -6,47 +6,55 @@
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import globals from 'globals';
|
||||
import js from '@eslint/js';
|
||||
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'amd': true,
|
||||
},
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
],
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 2018,
|
||||
"sourceType": "module",
|
||||
},
|
||||
'globals': {
|
||||
'_': true,
|
||||
'module': true,
|
||||
'process': true,
|
||||
'nw': true,
|
||||
'platform': true
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
2
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ['**/*.js'],
|
||||
ignores: [
|
||||
'generated',
|
||||
'node_modules',
|
||||
'vendor',
|
||||
'templates/',
|
||||
'templates\\',
|
||||
'ycache',
|
||||
],
|
||||
'linebreak-style': 0,
|
||||
'quotes': [
|
||||
'error',
|
||||
'single'
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'always-multiline'
|
||||
],
|
||||
'no-console': ["error", { allow: ["warn", "error"] }],
|
||||
// We need to exclude below for RegEx case
|
||||
"no-useless-escape": 0,
|
||||
languageOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.es2017,
|
||||
...globals.amd,
|
||||
'_': 'readonly',
|
||||
'module': 'readonly',
|
||||
'process': 'readonly',
|
||||
'platform': 'readonly',
|
||||
},
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
2,
|
||||
],
|
||||
'linebreak-style': 0,
|
||||
'quotes': [
|
||||
'error',
|
||||
'single',
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'always-multiline',
|
||||
],
|
||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
||||
// We need to exclude below for RegEx case
|
||||
'no-useless-escape': 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
];
|
||||
@@ -1,37 +1,25 @@
|
||||
{
|
||||
"name": "pgadmin4",
|
||||
"description": "pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.",
|
||||
"main": "src/html/pgadmin.html",
|
||||
"repository": "https://github.com/postgres/pgadmin4.git",
|
||||
"author": "pgAdmin Development Team (https://www.pgadmin.org/)",
|
||||
"license": "PostgreSQL",
|
||||
"chromium-args": "--disable-popup-blocking --disable-gpu --disable-devtools",
|
||||
"user-agent": "Nwjs:%nwver-%osinfo-%chromium_ver",
|
||||
"nodejs": true,
|
||||
"window": {
|
||||
"width": 750,
|
||||
"height": 600,
|
||||
"toolbar": false,
|
||||
"fullscreen": false,
|
||||
"frame": false,
|
||||
"show": true,
|
||||
"position": "center",
|
||||
"always-on-top": true,
|
||||
"icon": "assets/pgAdmin4.png"
|
||||
"type": "module",
|
||||
"main": "src/js/pgadmin.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"linter": "yarn run eslint -c .eslintrc.js ."
|
||||
},
|
||||
"icons": {
|
||||
"256": "assets/pgAdmin4.png"
|
||||
"packageManager": "yarn@3.8.2",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.8.0",
|
||||
"electron": "latest",
|
||||
"eslint": "^9.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"bootstrap": "^4.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^9.2.0",
|
||||
"nw": "0.77.0"
|
||||
},
|
||||
"scripts": {
|
||||
"linter": "yarn eslint --no-eslintrc -c .eslintrc.js --ext .js ."
|
||||
},
|
||||
"packageManager": "yarn@3.6.4"
|
||||
"axios": "^1.7.4",
|
||||
"bootstrap": "^4.5.3",
|
||||
"electron-context-menu": "^4.0.1",
|
||||
"electron-store": "^10.0.0"
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>pgAdmin 4 Configuration</title>
|
||||
<title>pgAdmin 4 Runtime Configuration</title>
|
||||
<link rel="stylesheet" href="../css/pgadmin-desktop.css"/>
|
||||
<style>
|
||||
body, html {
|
||||
@@ -76,6 +76,99 @@
|
||||
<button id="btnSave" type="submit" class="btn btn-primary" disabled>Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../js/configure.js"></script>
|
||||
<script type="text/javascript" >
|
||||
let configData;
|
||||
|
||||
async function checkConfiguration() {
|
||||
const portNo = configData.portNo;
|
||||
|
||||
if (document.getElementById('fixedPortCheck').checked && portNo !== document.getElementById('portNo').value) {
|
||||
let fixedPort = parseInt(document.getElementById('portNo').value);
|
||||
// get the available TCP port
|
||||
if(await window.electronUI.checkPortAvailable(fixedPort)) {
|
||||
saveConfiguration();
|
||||
} else {
|
||||
alert('The port specified is already in use. Please enter a free port number.');
|
||||
}
|
||||
} else {
|
||||
saveConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
async function saveConfiguration() {
|
||||
await window.electronUI.setConfigData({
|
||||
'fixedPort': document.getElementById('fixedPortCheck').checked,
|
||||
'portNo': parseInt(document.getElementById('portNo').value),
|
||||
'connectionTimeout': parseInt(document.getElementById('timeOut').value),
|
||||
'openDocsInBrowser': document.getElementById('openDocsInBrowser').checked
|
||||
});
|
||||
|
||||
document.getElementById('status-text').innerHTML = 'Configuration Saved';
|
||||
const result = await window.electronUI.showMessageBox({
|
||||
'type': 'question',
|
||||
'title': 'Confirmation',
|
||||
'message': "pgAdmin 4 must be restarted for changes to take effect.\n\n Do you want to quit the application?",
|
||||
'buttons': [
|
||||
'Yes',
|
||||
'No'
|
||||
]
|
||||
});
|
||||
if(result.response === 0) {
|
||||
window.electronUI.restartApp();
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
|
||||
function onCheckChange() {
|
||||
if (this.checked) {
|
||||
document.getElementById('portNo').removeAttribute('disabled');
|
||||
} else {
|
||||
document.getElementById('portNo').setAttribute('disabled', 'disabled');
|
||||
}
|
||||
|
||||
// Enable/Disable Save button
|
||||
enableDisableSaveButton();
|
||||
}
|
||||
|
||||
function enableDisableSaveButton() {
|
||||
if (configData['fixedPort'] !== document.getElementById('fixedPortCheck').checked ||
|
||||
configData['portNo'] != document.getElementById('portNo').value ||
|
||||
configData['connectionTimeout'] != document.getElementById('timeOut').value ||
|
||||
configData['openDocsInBrowser'] !== document.getElementById('openDocsInBrowser').checked) {
|
||||
document.getElementById('btnSave').removeAttribute('disabled');
|
||||
} else {
|
||||
document.getElementById('btnSave').setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = async function() {
|
||||
configData = await window.electronUI.getConfigData();
|
||||
document.getElementById('status-text').innerHTML = '';
|
||||
// Set the GUI value as per configuration.
|
||||
if (configData['fixedPort']) {
|
||||
document.getElementById('fixedPortCheck').checked = true;
|
||||
document.getElementById('portNo').disabled = false;
|
||||
} else {
|
||||
document.getElementById('fixedPortCheck').checked = false;
|
||||
document.getElementById('portNo').disabled = true;
|
||||
}
|
||||
document.getElementById('portNo').value = configData['portNo'];
|
||||
document.getElementById('timeOut').value = configData['connectionTimeout'];
|
||||
|
||||
if (configData['openDocsInBrowser']) {
|
||||
document.getElementById('openDocsInBrowser').checked = true;
|
||||
} else {
|
||||
document.getElementById('openDocsInBrowser').checked = false;
|
||||
}
|
||||
// Add event listeners
|
||||
document.getElementById('btnSave').addEventListener('click', checkConfiguration);
|
||||
document.getElementById('fixedPortCheck').addEventListener('change', onCheckChange);
|
||||
document.getElementById('portNo').addEventListener('change', enableDisableSaveButton);
|
||||
document.getElementById('timeOut').addEventListener('change', enableDisableSaveButton);
|
||||
document.getElementById('openDocsInBrowser').addEventListener('change', enableDisableSaveButton);
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -31,6 +31,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../js/server_error.js"></script>
|
||||
<script>
|
||||
window.onload = async function() {
|
||||
document.getElementById('server_error_label').innerHTML = 'The pgAdmin 4 server could not be contacted:';
|
||||
document.getElementById('server_error_log').innerHTML = await window.electronUI.readServerLog();
|
||||
document.getElementById('btnConfigure').addEventListener('click', function() {
|
||||
window.electronUI.openConfigure();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -19,6 +19,7 @@
|
||||
background-size: contain;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.loader-text {
|
||||
@@ -50,9 +51,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg">
|
||||
<span id="loader-text-status" class="loader-text"></span>
|
||||
<span id="loader-text-status" class="loader-text">Waiting for pgAdmin 4 to start...</span>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../js/pgadmin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -31,6 +31,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../js/view_log.js"></script>
|
||||
<script>
|
||||
window.onload = async function() {
|
||||
document.getElementById('status-text').innerHTML = '';
|
||||
document.getElementById('server_log_label').innerHTML = 'Server Log: ' + '(' + await window.electronUI.getServerLogFile() + ')';
|
||||
document.getElementById('server_log').innerHTML = await window.electronUI.readServerLog();
|
||||
document.getElementById('btnReload').addEventListener('click', function() {
|
||||
document.getElementById('server_log').innerHTML = 'Loading logs...';
|
||||
setTimeout(async function() {
|
||||
document.getElementById('server_log').innerHTML = await window.electronUI.readServerLog();
|
||||
}, 500);
|
||||
document.getElementById('status-text').innerHTML = 'Logs reloaded successfully';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,101 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
const misc = require('../js/misc.js');
|
||||
|
||||
// Get the window object of view log window
|
||||
let gui = require('nw.gui');
|
||||
let configWindow = gui.Window.get();
|
||||
|
||||
function checkConfiguration() {
|
||||
let configData = misc.ConfigureStore.getConfigData();
|
||||
|
||||
if (document.getElementById('fixedPortCheck').checked && configData['portNo'] !== document.getElementById('portNo').value) {
|
||||
let fixedPort = parseInt(document.getElementById('portNo').value);
|
||||
// get the available TCP port
|
||||
misc.getAvailablePort(fixedPort)
|
||||
.then(() => {
|
||||
saveConfiguration();
|
||||
})
|
||||
.catch(() => {
|
||||
alert('The port specified is already in use. Please enter a free port number.');
|
||||
});
|
||||
} else {
|
||||
saveConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
function saveConfiguration() {
|
||||
misc.ConfigureStore.set('fixedPort', document.getElementById('fixedPortCheck').checked);
|
||||
misc.ConfigureStore.set('portNo', parseInt(document.getElementById('portNo').value));
|
||||
misc.ConfigureStore.set('connectionTimeout', parseInt(document.getElementById('timeOut').value));
|
||||
misc.ConfigureStore.set('openDocsInBrowser', document.getElementById('openDocsInBrowser').checked);
|
||||
|
||||
misc.ConfigureStore.saveConfig();
|
||||
|
||||
document.getElementById('status-text').innerHTML = 'Configuration Saved';
|
||||
|
||||
if (confirm('pgAdmin 4 must be restarted for changes to take effect.\n\n Do you want to quit the application?') === true) {
|
||||
misc.cleanupAndQuitApp();
|
||||
}
|
||||
configWindow.close();
|
||||
}
|
||||
|
||||
function onCheckChange() {
|
||||
if (this.checked) {
|
||||
document.getElementById('portNo').removeAttribute('disabled');
|
||||
} else {
|
||||
document.getElementById('portNo').setAttribute('disabled', 'disabled');
|
||||
}
|
||||
|
||||
// Enable/Disable Save button
|
||||
enableDisableSaveButton();
|
||||
}
|
||||
|
||||
function enableDisableSaveButton() {
|
||||
let configData = misc.ConfigureStore.getConfigData();
|
||||
|
||||
if (configData['fixedPort'] !== document.getElementById('fixedPortCheck').checked ||
|
||||
configData['portNo'] != document.getElementById('portNo').value ||
|
||||
configData['connectionTimeout'] != document.getElementById('timeOut').value ||
|
||||
configData['openDocsInBrowser'] !== document.getElementById('openDocsInBrowser').checked) {
|
||||
document.getElementById('btnSave').removeAttribute('disabled');
|
||||
} else {
|
||||
document.getElementById('btnSave').setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
configWindow.on('loaded', function() {
|
||||
document.getElementById('status-text').innerHTML = '';
|
||||
// Get the config data from the file.
|
||||
let configData = misc.ConfigureStore.getConfigData();
|
||||
|
||||
// Set the GUI value as per configuration.
|
||||
if (configData['fixedPort']) {
|
||||
document.getElementById('fixedPortCheck').checked = true;
|
||||
document.getElementById('portNo').disabled = false;
|
||||
} else {
|
||||
document.getElementById('fixedPortCheck').checked = false;
|
||||
document.getElementById('portNo').disabled = true;
|
||||
}
|
||||
document.getElementById('portNo').value = configData['portNo'];
|
||||
document.getElementById('timeOut').value = configData['connectionTimeout'];
|
||||
|
||||
if (configData['openDocsInBrowser']) {
|
||||
document.getElementById('openDocsInBrowser').checked = true;
|
||||
} else {
|
||||
document.getElementById('openDocsInBrowser').checked = false;
|
||||
}
|
||||
// Add event listeners
|
||||
document.getElementById('btnSave').addEventListener('click', checkConfiguration);
|
||||
document.getElementById('fixedPortCheck').addEventListener('change', onCheckChange);
|
||||
document.getElementById('portNo').addEventListener('change', enableDisableSaveButton);
|
||||
document.getElementById('timeOut').addEventListener('change', enableDisableSaveButton);
|
||||
document.getElementById('openDocsInBrowser').addEventListener('change', enableDisableSaveButton);
|
||||
});
|
||||
@@ -0,0 +1,111 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { app, Menu, ipcMain, BrowserWindow } from 'electron';
|
||||
|
||||
const isMac = process.platform == 'darwin';
|
||||
const isLinux = process.platform == 'linux';
|
||||
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' },
|
||||
...(isMac ? [
|
||||
{ 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: ()=>BrowserWindow.getFocusedWindow().webContents.reload()},
|
||||
{ label: 'Toggle Developer Tools', click: ()=>BrowserWindow.getFocusedWindow().webContents.openDevTools({ mode: 'bottom' })},
|
||||
{ type: 'separator' },
|
||||
{ role: 'resetZoom' },
|
||||
{ role: 'zoomIn' },
|
||||
{ role: 'zoomOut' },
|
||||
{ type: 'separator' },
|
||||
].concat(isLinux ? [] : [{ role: 'togglefullscreen' }]),
|
||||
},
|
||||
{ role: 'windowMenu' },
|
||||
);
|
||||
|
||||
template.push(pgadminMenus[pgadminMenus.length-1]);
|
||||
|
||||
return Menu.buildFromTemplate(template);
|
||||
}
|
||||
|
||||
export function setupMenu(pgAdminMainScreen, callbacks={}) {
|
||||
ipcMain.on('setMenus', (event, menus)=>{
|
||||
mainMenu = buildMenu(menus, pgAdminMainScreen, callbacks);
|
||||
if(isMac) {
|
||||
Menu.setApplicationMenu(mainMenu);
|
||||
} else {
|
||||
pgAdminMainScreen.setMenu(mainMenu);
|
||||
}
|
||||
|
||||
ipcMain.on('enable-disable-menu-items', (event, menu, menuItem)=>{
|
||||
const menuItemObj = mainMenu.getMenuItemById(menuItem?.id);
|
||||
if(menuItemObj) {
|
||||
menuItemObj.enabled = menuItem.isDisabled;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -7,16 +7,13 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const net = require('net');
|
||||
const {platform, homedir} = require('os');
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import net from 'net';
|
||||
import {platform} from 'os';
|
||||
import { app, session } from 'electron';
|
||||
|
||||
let pgadminServerProcess = null;
|
||||
let pgAdminWindowObject = null;
|
||||
let zoomInShortcut = null;
|
||||
let zoomOutShortcut = null;
|
||||
let actualSizeShortcut = null;
|
||||
let toggleFullScreenShortcut = null;
|
||||
|
||||
// This function is used to check whether directory is present or not
|
||||
// if not present then create it recursively
|
||||
@@ -26,21 +23,34 @@ const createDir = (dirName) => {
|
||||
}
|
||||
};
|
||||
|
||||
const insideFlatpak = () => {
|
||||
return platform() === 'linux' && fs.existsSync('/.flatpak-info');
|
||||
};
|
||||
|
||||
// This function is used to get the python executable path
|
||||
// based on the platform. Use this for deployment.
|
||||
const getPythonPath = () => {
|
||||
let pythonPath;
|
||||
export const getAppPaths = (basePath) => {
|
||||
let pythonPath, pgadminFile;
|
||||
switch (platform()) {
|
||||
case 'win32':
|
||||
pythonPath = '../python/python.exe';
|
||||
pythonPath = '../../../../../python/python.exe';
|
||||
pgadminFile = '../../../../../web/pgAdmin4.py';
|
||||
break;
|
||||
case 'darwin':
|
||||
pythonPath = '../../Frameworks/Python.framework/Versions/Current/bin/python3';
|
||||
pythonPath = '../../../../Frameworks/Python.framework/Versions/Current/bin/python3';
|
||||
pgadminFile = '../../../web/pgAdmin4.py';
|
||||
break;
|
||||
case 'linux':
|
||||
pythonPath = '../venv/bin/python3';
|
||||
pythonPath = '../../../../../venv/bin/python3';
|
||||
pgadminFile = '../../../../../web/pgAdmin4.py';
|
||||
if (insideFlatpak()) {
|
||||
pythonPath = '/usr/bin/python';
|
||||
pgadminFile = '/app/pgAdmin4/web/pgAdmin4.py';
|
||||
return [pythonPath, pgadminFile];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pgadminFile = '../../../web/pgAdmin4.py';
|
||||
if (platform().startsWith('win')) {
|
||||
pythonPath = '../python/python.exe';
|
||||
} else {
|
||||
@@ -48,69 +58,15 @@ const getPythonPath = () => {
|
||||
}
|
||||
}
|
||||
|
||||
return pythonPath;
|
||||
};
|
||||
|
||||
// This function is used to get the [roaming] app data path
|
||||
// based on the platform. Use this for config etc.
|
||||
const getAppDataPath = () => {
|
||||
let appDataPath;
|
||||
switch (platform()) {
|
||||
case 'win32':
|
||||
appDataPath = path.join(process.env.APPDATA, 'pgadmin');
|
||||
break;
|
||||
case 'darwin':
|
||||
appDataPath = path.join(homedir(), 'Library', 'Preferences', 'pgadmin');
|
||||
break;
|
||||
case 'linux':
|
||||
if ('XDG_CONFIG_HOME' in process.env) {
|
||||
appDataPath = path.join(process.env.XDG_CONFIG_HOME, 'pgadmin');
|
||||
} else {
|
||||
appDataPath = path.join(homedir(), '.config', 'pgadmin');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (platform().startsWith('win')) {
|
||||
appDataPath = path.join(process.env.APPDATA, 'pgadmin');
|
||||
} else if ('XDG_CONFIG_HOME' in process.env) {
|
||||
appDataPath = path.join(process.env.XDG_CONFIG_HOME, 'pgadmin');
|
||||
} else {
|
||||
appDataPath = path.join(homedir(), '.config', 'pgadmin');
|
||||
}
|
||||
}
|
||||
|
||||
// Create directory if not exists
|
||||
createDir(appDataPath);
|
||||
|
||||
return appDataPath;
|
||||
return [path.join(basePath, pythonPath), path.join(basePath, pgadminFile)];
|
||||
};
|
||||
|
||||
// This function is used to get the [local] app data path
|
||||
// based on the platform. Use this for logs etc.
|
||||
const getLocalAppDataPath = () => {
|
||||
let localAppDataPath;
|
||||
switch (platform()) {
|
||||
case 'win32':
|
||||
localAppDataPath = path.join(process.env.LOCALAPPDATA, 'pgadmin');
|
||||
break;
|
||||
case 'darwin':
|
||||
localAppDataPath = path.join(homedir(), 'Library', 'Application Support', 'pgadmin');
|
||||
break;
|
||||
case 'linux':
|
||||
if ('XDG_DATA_HOME' in process.env) {
|
||||
localAppDataPath = path.join(process.env.XDG_DATA_HOME, 'pgadmin');
|
||||
} else {
|
||||
localAppDataPath = path.join(homedir(), '.local', 'share', 'pgadmin');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (platform().startsWith('win')) {
|
||||
localAppDataPath = path.join(process.env.LOCALAPPDATA, 'pgadmin');
|
||||
} else if ('XDG_DATA_HOME' in process.env) {
|
||||
localAppDataPath = path.join(process.env.XDG_DATA_HOME, 'pgadmin');
|
||||
} else {
|
||||
localAppDataPath = path.join(homedir(), '.local', 'share', 'pgadmin');
|
||||
}
|
||||
let localAppDataPath = app.getPath('userData');
|
||||
if(process.platform == 'linux' && 'XDG_DATA_HOME' in process.env) {
|
||||
localAppDataPath = path.join(process.env.XDG_DATA_HOME, app.name);
|
||||
}
|
||||
|
||||
// Create directory if not exists
|
||||
@@ -121,14 +77,14 @@ const getLocalAppDataPath = () => {
|
||||
|
||||
// This function is used to get the random available TCP port
|
||||
// if fixedPort is set to 0. Else check whether port is in used or not.
|
||||
const getAvailablePort = (fixedPort) => {
|
||||
export const getAvailablePort = (fixedPort) => {
|
||||
return new Promise(function(resolve, reject) {
|
||||
const server = net.createServer();
|
||||
|
||||
server.listen(fixedPort, '127.0.0.1');
|
||||
|
||||
server.on('error', (e) => {
|
||||
reject(e.code);
|
||||
reject(e instanceof Error ? e : new Error(e.code));
|
||||
});
|
||||
|
||||
server.on('listening', () => {
|
||||
@@ -140,13 +96,10 @@ const getAvailablePort = (fixedPort) => {
|
||||
};
|
||||
|
||||
// Get the app data folder path
|
||||
const currentTime = (new Date()).getTime();
|
||||
const serverLogFile = path.join(getLocalAppDataPath(), 'pgadmin4.' + currentTime.toString() + '.log');
|
||||
const configFileName = path.join(getAppDataPath(), 'runtime_config.json');
|
||||
const DEFAULT_CONFIG_DATA = {'fixedPort': false, 'portNo': 5050, 'connectionTimeout': 90, 'zoomLevel': 0, 'openDocsInBrowser': true};
|
||||
const serverLogFile = path.join(getLocalAppDataPath(), 'pgadmin4.' + (new Date()).getTime().toString() + '.log');
|
||||
|
||||
// This function is used to read the file and return the content
|
||||
const readServerLog = () => {
|
||||
export const readServerLog = () => {
|
||||
let data = null;
|
||||
|
||||
if (fs.existsSync(serverLogFile)) {
|
||||
@@ -161,7 +114,7 @@ const readServerLog = () => {
|
||||
};
|
||||
|
||||
// This function is used to write the data into the log file
|
||||
const writeServerLog = (data) => {
|
||||
export const writeServerLog = (data) => {
|
||||
data = data + '\n';
|
||||
if (fs.existsSync(serverLogFile)) {
|
||||
fs.writeFileSync(serverLogFile, data, {flag: 'a+'});
|
||||
@@ -178,28 +131,18 @@ const removeLogFile = () => {
|
||||
};
|
||||
|
||||
// This function used to set the object of pgAdmin server process.
|
||||
const setProcessObject = (processObject) => {
|
||||
export const setProcessObject = (processObject) => {
|
||||
pgadminServerProcess = processObject;
|
||||
};
|
||||
|
||||
// This function used to set the object of pgAdmin window.
|
||||
const setPgAdminWindowObject = (windowObject) => {
|
||||
pgAdminWindowObject = windowObject;
|
||||
};
|
||||
|
||||
// This function is used to get the server log file.
|
||||
const getServerLogFile = () => {
|
||||
export const getServerLogFile = () => {
|
||||
return serverLogFile;
|
||||
};
|
||||
|
||||
// This function is used to get the runtime config file.
|
||||
const getRunTimeConfigFile = () => {
|
||||
return configFileName;
|
||||
};
|
||||
|
||||
// This function is used to kill the server process, remove the log files
|
||||
// and quit the application.
|
||||
const cleanupAndQuitApp = () => {
|
||||
export const cleanupAndQuitApp = () => {
|
||||
// Remove the server log file on exit
|
||||
removeLogFile();
|
||||
|
||||
@@ -209,246 +152,11 @@ const cleanupAndQuitApp = () => {
|
||||
process.kill(pgadminServerProcess.pid);
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Failed to kill server process.');
|
||||
console.warn('Failed to kill server process.', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (pgAdminWindowObject != null) {
|
||||
// Remove all the cookies.
|
||||
pgAdminWindowObject.cookies.getAll({}, function(cookies) {
|
||||
try {
|
||||
cookies.forEach(function(cookie) {
|
||||
pgAdminWindowObject.cookies.remove({url: 'http://' + cookie.domain, name: cookie.name});
|
||||
});
|
||||
} 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);
|
||||
session.defaultSession.clearStorageData({
|
||||
storages: ['cookies', 'localstorage'],
|
||||
});
|
||||
};
|
||||
|
||||
// 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,
|
||||
};
|
||||
@@ -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,40 +6,115 @@
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const misc = require('../js/misc.js');
|
||||
const spawn = require('child_process').spawn;
|
||||
const {EOL} = require('os');
|
||||
import { app, BrowserWindow, dialog, ipcMain, Menu, shell } from 'electron';
|
||||
import axios from 'axios';
|
||||
import Store from 'electron-store';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import * as misc from './misc.js';
|
||||
import { spawn } from 'child_process';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { setupMenu } from './menu.js';
|
||||
import contextMenu from 'electron-context-menu';
|
||||
|
||||
const configStore = new Store({
|
||||
defaults: {
|
||||
fixedPort: false,
|
||||
portNo: 5050,
|
||||
connectionTimeout: 180,
|
||||
openDocsInBrowser: true,
|
||||
},
|
||||
});
|
||||
let pgadminServerProcess = null;
|
||||
let startPageUrl = null;
|
||||
let serverCheckUrl = null;
|
||||
let addMenuCompleted = false;
|
||||
let pgAdminMainScreen = null;
|
||||
|
||||
let serverPort = 5050;
|
||||
let appStartTime = (new Date()).getTime();
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
let docsURLSubStrings = ['www.enterprisedb.com', 'www.postgresql.org', 'www.pgadmin.org', 'help/help'];
|
||||
|
||||
process.env['ELECTRON_ENABLE_SECURITY_WARNINGS'] = false;
|
||||
|
||||
// Paths to the rest of the app
|
||||
let pythonPath = misc.getPythonPath();
|
||||
let pgadminFile = '../web/pgAdmin4.py';
|
||||
let configFile = '../web/config.py';
|
||||
let [pythonPath, pgadminFile] = misc.getAppPaths(__dirname);
|
||||
|
||||
// Do not allow a second instance of pgAdmin to run.
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
if (!gotTheLock) {
|
||||
app.quit();
|
||||
} else {
|
||||
app.on('second-instance', () => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (pgAdminMainScreen) {
|
||||
if (pgAdminMainScreen.isMinimized()) pgAdminMainScreen.restore();
|
||||
pgAdminMainScreen.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Override the paths above, if a developer needs to
|
||||
if (fs.existsSync('dev_config.json')) {
|
||||
try {
|
||||
let dev_config = JSON.parse(fs.readFileSync('dev_config.json'));
|
||||
pythonPath = dev_config['pythonPath'];
|
||||
pgadminFile = dev_config['pgadminFile'];
|
||||
pythonPath = path.resolve(dev_config['pythonPath']);
|
||||
pgadminFile = path.resolve(dev_config['pgadminFile']);
|
||||
} catch (error) {
|
||||
// Meh.
|
||||
console.error('Failed to load dev_config', error);
|
||||
}
|
||||
}
|
||||
|
||||
contextMenu({
|
||||
showInspectElement: false,
|
||||
showSearchWithGoogle: false,
|
||||
showLookUpSelection: false,
|
||||
showSelectAll: true,
|
||||
});
|
||||
|
||||
Menu.setApplicationMenu(null);
|
||||
|
||||
function openConfigure() {
|
||||
const win = new BrowserWindow({
|
||||
show: false,
|
||||
width: 600,
|
||||
height: 620,
|
||||
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': 800,
|
||||
'height': 450,
|
||||
'position': 'center',
|
||||
'resizable': false,
|
||||
'focus': true,
|
||||
'show': true,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'other_preload.js'),
|
||||
},
|
||||
}).loadFile('./src/html/server_error.html');
|
||||
}
|
||||
|
||||
// This functions is used to start the pgAdmin4 server by spawning a
|
||||
// separate process.
|
||||
function startDesktopMode() {
|
||||
@@ -48,6 +123,7 @@ function startDesktopMode() {
|
||||
if (pgadminServerProcess != null)
|
||||
return;
|
||||
|
||||
let pingIntervalID;
|
||||
let UUID = crypto.randomUUID();
|
||||
// Set the environment variables so that pgAdmin 4 server
|
||||
// starts listening on the appropriate port.
|
||||
@@ -59,28 +135,25 @@ function startDesktopMode() {
|
||||
startPageUrl = 'http://127.0.0.1:' + serverPort + '/?key=' + UUID;
|
||||
serverCheckUrl = 'http://127.0.0.1:' + serverPort + '/misc/ping?key=' + UUID;
|
||||
|
||||
document.getElementById('loader-text-status').innerHTML = 'Starting pgAdmin 4...';
|
||||
|
||||
// Write Python Path, pgAdmin file path and command in log file.
|
||||
misc.writeServerLog('pgAdmin Runtime Environment');
|
||||
misc.writeServerLog('--------------------------------------------------------');
|
||||
let command = path.resolve(pythonPath) + ' -s ' + path.resolve(pgadminFile);
|
||||
misc.writeServerLog('Python Path: "' + path.resolve(pythonPath) + '"');
|
||||
misc.writeServerLog('Runtime Config File: "' + path.resolve(misc.getRunTimeConfigFile()) + '"');
|
||||
misc.writeServerLog('pgAdmin Config File: "' + path.resolve(configFile) + '"');
|
||||
misc.writeServerLog('Webapp Path: "' + path.resolve(pgadminFile) + '"');
|
||||
let command = pythonPath + ' -s ' + pgadminFile;
|
||||
misc.writeServerLog('Python Path: "' + pythonPath + '"');
|
||||
misc.writeServerLog('Runtime Config File: "' + path.resolve(configStore.path) + '"');
|
||||
misc.writeServerLog('Webapp Path: "' + pgadminFile + '"');
|
||||
misc.writeServerLog('pgAdmin Command: "' + command + '"');
|
||||
misc.writeServerLog('Environment: ');
|
||||
Object.keys(process.env).forEach(function (key) {
|
||||
// Below code is included only for Mac OS as default path for azure CLI
|
||||
// installation path is not included in PATH variable while spawning
|
||||
// runtime environment.
|
||||
if (platform() === 'darwin' && key === 'PATH') {
|
||||
if (process.platform === 'darwin' && key === 'PATH') {
|
||||
let updated_path = process.env[key] + ':/usr/local/bin';
|
||||
process.env[key] = updated_path;
|
||||
}
|
||||
|
||||
if (platform() === 'win32' && key.toUpperCase() === 'PATH') {
|
||||
if (process.platform === 'win32' && key.toUpperCase() === 'PATH') {
|
||||
let _libpq_path = path.join(path.dirname(path.dirname(path.resolve(pgadminFile))), 'runtime');
|
||||
process.env[key] = _libpq_path + ';' + process.env[key];
|
||||
}
|
||||
@@ -91,12 +164,14 @@ function startDesktopMode() {
|
||||
|
||||
// Spawn the process to start pgAdmin4 server.
|
||||
let spawnStartTime = (new Date).getTime();
|
||||
pgadminServerProcess = spawn(path.resolve(pythonPath), ['-s', path.resolve(pgadminFile)]);
|
||||
pgadminServerProcess = spawn(pythonPath, ['-s', pgadminFile]);
|
||||
pgadminServerProcess.on('error', function (err) {
|
||||
// Log the error into the log file if process failed to launch
|
||||
misc.writeServerLog('Failed to launch pgAdmin4. Error:');
|
||||
misc.writeServerLog(err);
|
||||
showErrorDialog(pingIntervalID);
|
||||
});
|
||||
|
||||
let spawnEndTime = (new Date).getTime();
|
||||
misc.writeServerLog('Total spawn time to start the pgAdmin4 server: ' + (spawnEndTime - spawnStartTime) / 1000 + ' Sec');
|
||||
|
||||
@@ -116,7 +191,7 @@ function startDesktopMode() {
|
||||
return axios.get(serverCheckUrl);
|
||||
}
|
||||
|
||||
let connectionTimeout = misc.ConfigureStore.get('connectionTimeout', 90) * 1000;
|
||||
let connectionTimeout = configStore.get('connectionTimeout', 180) * 1000;
|
||||
let currentTime = (new Date).getTime();
|
||||
let endTime = currentTime + connectionTimeout;
|
||||
let midTime1 = currentTime + (connectionTimeout / 2);
|
||||
@@ -125,7 +200,7 @@ function startDesktopMode() {
|
||||
|
||||
// ping pgAdmin server every 1 second.
|
||||
let pingStartTime = (new Date).getTime();
|
||||
let intervalID = setInterval(function () {
|
||||
pingIntervalID = setInterval(function () {
|
||||
// If ping request is already send and response is not
|
||||
// received no need to send another request.
|
||||
if (pingInProgress)
|
||||
@@ -133,11 +208,11 @@ function startDesktopMode() {
|
||||
|
||||
pingServer().then(() => {
|
||||
pingInProgress = false;
|
||||
document.getElementById('loader-text-status').innerHTML = 'pgAdmin 4 started';
|
||||
splashWindow.webContents.executeJavaScript('document.getElementById(\'loader-text-status\').innerHTML = \'pgAdmin 4 started\';', true);
|
||||
// Set the pgAdmin process object to misc
|
||||
misc.setProcessObject(pgadminServerProcess);
|
||||
|
||||
clearInterval(intervalID);
|
||||
clearInterval(pingIntervalID);
|
||||
let appEndTime = (new Date).getTime();
|
||||
misc.writeServerLog('------------------------------------------');
|
||||
misc.writeServerLog('Total time taken to ping pgAdmin4 server: ' + (appEndTime - pingStartTime) / 1000 + ' Sec');
|
||||
@@ -151,33 +226,20 @@ function startDesktopMode() {
|
||||
// if the connection timeout has lapsed then throw an error
|
||||
// and stop pinging the server.
|
||||
if (curTime >= endTime) {
|
||||
clearInterval(intervalID);
|
||||
splashWindow.hide();
|
||||
|
||||
nw.Window.open('src/html/server_error.html', {
|
||||
'frame': true,
|
||||
'width': 790,
|
||||
'height': 430,
|
||||
'position': 'center',
|
||||
'resizable': false,
|
||||
'focus': true,
|
||||
'show': true,
|
||||
});
|
||||
showErrorDialog(pingIntervalID);
|
||||
}
|
||||
|
||||
if (curTime > midTime1) {
|
||||
if (curTime < midTime2) {
|
||||
document.getElementById('loader-text-status').innerHTML = 'Taking longer than usual...';
|
||||
splashWindow.webContents.executeJavaScript('document.getElementById(\'loader-text-status\').innerHTML = \'Taking longer than usual...\';', true);
|
||||
} else {
|
||||
document.getElementById('loader-text-status').innerHTML = 'Almost there...';
|
||||
splashWindow.webContents.executeJavaScript('document.getElementById(\'loader-text-status\').innerHTML = \'Almost there...\';', true);
|
||||
}
|
||||
} else {
|
||||
document.getElementById('loader-text-status').innerHTML = 'Waiting for pgAdmin 4 to start...';
|
||||
}
|
||||
});
|
||||
|
||||
pingInProgress = true;
|
||||
}, 250);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// This function is used to hide the splash screen and create/launch
|
||||
@@ -185,7 +247,7 @@ function startDesktopMode() {
|
||||
function launchPgAdminWindow() {
|
||||
// Create and launch new window and open pgAdmin url
|
||||
misc.writeServerLog('Application Server URL: ' + startPageUrl);
|
||||
nw.Window.open(startPageUrl, {
|
||||
pgAdminMainScreen = new BrowserWindow({
|
||||
'id': 'pgadmin-main',
|
||||
'icon': '../../assets/pgAdmin4.png',
|
||||
'frame': true,
|
||||
@@ -197,503 +259,153 @@ function launchPgAdminWindow() {
|
||||
'height': 768,
|
||||
'focus': true,
|
||||
'show': false,
|
||||
}, (pgadminWindow) => {
|
||||
pgAdminMainScreen = pgadminWindow;
|
||||
// Set pgAdmin4 Windows Object
|
||||
misc.setPgAdminWindowObject(pgadminWindow);
|
||||
|
||||
// Set the zoom level stored in the config file.
|
||||
pgadminWindow.zoomLevel = misc.ConfigureStore.get('zoomLevel', 0);
|
||||
|
||||
// Set zoom in and out events.
|
||||
misc.setZoomEvents();
|
||||
|
||||
// Workaround to fix increasing window size.
|
||||
// https://github.com/nwjs/nw.js/issues/7973
|
||||
pgadminWindow.on('close', function () {
|
||||
// Resize Window
|
||||
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 () {
|
||||
misc.cleanupAndQuitApp();
|
||||
});
|
||||
|
||||
// set up handler for new-win-policy event.
|
||||
// Set the width and height for the new window.
|
||||
pgadminWindow.on('new-win-policy', function (frame, url, policy) {
|
||||
if (!frame) {
|
||||
let openDocsInBrowser = misc.ConfigureStore.get('openDocsInBrowser', true);
|
||||
let isDocURL = false;
|
||||
docsURLSubStrings.forEach(function (key) {
|
||||
if (url.indexOf(key) >= 0) {
|
||||
isDocURL = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
if (openDocsInBrowser && isDocURL) {
|
||||
// Do not open the window
|
||||
policy.ignore();
|
||||
// Open URL in the external browser.
|
||||
nw.Shell.openExternal(url);
|
||||
} else {
|
||||
policy.setNewWindowManifest({
|
||||
'icon': '../../assets/pgAdmin4.png',
|
||||
'frame': true,
|
||||
'position': 'center',
|
||||
'min_width': 640,
|
||||
'min_height': 480,
|
||||
'width': pgadminWindow.width,
|
||||
'height': pgadminWindow.height,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pgadminWindow.on('loaded', function () {
|
||||
/* Make the new window opener to null as it is
|
||||
* nothing but a splash screen. We will have to make it null,
|
||||
* so that open in new browser tab will work.
|
||||
*/
|
||||
pgadminWindow.window.hookConsole((method, args)=>{
|
||||
misc.writeServerLog(
|
||||
`--------------[UI ${method}]---------------${EOL}${misc.parseConsoleArgs(method, args)}${EOL}------------[UI End]----------------`);
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
try {
|
||||
pgAdminMainScreen.isCustomMenusAdded = false;
|
||||
let addMenuInterval = setInterval(() => {
|
||||
if (pgadminWindow?.window?.pgAdmin?.Browser?.Events && pgadminWindow?.window?.pgAdmin?.Browser?.MainMenus?.length > 0) {
|
||||
pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-enable-disable-menu-items', enableDisableMenuItem);
|
||||
pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-refresh-menu-item', refreshMenuItems);
|
||||
pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-update-checked-menu-item', updateCheckedMenuItem);
|
||||
pgadminWindow.window.pgAdmin.Browser.Events.on('pgadmin:nw-set-new-window-open-size', setNewWindowSize)
|
||||
// Add Main Menus to native menu.
|
||||
pgadminWindow.window.pgAdmin.Browser.MainMenus.forEach((menu)=> {
|
||||
addMenu(menu)
|
||||
})
|
||||
clearInterval(addMenuInterval);
|
||||
}
|
||||
}, 250)
|
||||
} catch (e) {
|
||||
console.error('Error in add native menus');
|
||||
}
|
||||
|
||||
// Hide the splash screen
|
||||
splashWindow.hide();
|
||||
});
|
||||
|
||||
pgadminWindow.on('blur', function () {
|
||||
misc.unregisterZoomEvents();
|
||||
});
|
||||
|
||||
pgadminWindow.on('focus', function () {
|
||||
misc.registerZoomEvents();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Get the gui object of NW.js
|
||||
let gui = require('nw.gui');
|
||||
let splashWindow = gui.Window.get();
|
||||
|
||||
// Enable dragging on the splash screen.
|
||||
let isDragging = false;
|
||||
let dragOrigin = { x: 0, y: 0 };
|
||||
document.mouseleave = () => isDragging = false;
|
||||
document.onmouseup = () => isDragging = false;
|
||||
|
||||
document.onmousedown = (e) => {
|
||||
isDragging = true;
|
||||
dragOrigin.x = e.x;
|
||||
dragOrigin.y = e.y;
|
||||
};
|
||||
|
||||
document.onmousemove = (e) => {
|
||||
if (isDragging) {
|
||||
splashWindow.moveTo(e.screenX - dragOrigin.x, e.screenY - dragOrigin.y);
|
||||
}
|
||||
};
|
||||
|
||||
// Always clear the cache before starting the application.
|
||||
nw.App.clearCache();
|
||||
|
||||
let nativeMenu;
|
||||
|
||||
splashWindow.on('loaded', function () {
|
||||
// Initialize the ConfigureStore
|
||||
misc.ConfigureStore.init();
|
||||
|
||||
let fixedPortCheck = misc.ConfigureStore.get('fixedPort', false);
|
||||
if (fixedPortCheck) {
|
||||
serverPort = misc.ConfigureStore.get('portNo');
|
||||
//Start the pgAdmin in Desktop mode.
|
||||
startDesktopMode();
|
||||
} else {
|
||||
// get the available TCP port by sending port no to 0.
|
||||
misc.getAvailablePort(0)
|
||||
.then((pythonApplicationPort) => {
|
||||
serverPort = pythonApplicationPort;
|
||||
//Start the pgAdmin in Desktop mode.
|
||||
startDesktopMode();
|
||||
})
|
||||
.catch((errCode) => {
|
||||
if (errCode === 'EADDRINUSE') {
|
||||
alert('The port specified is already in use. Please enter a free port number.');
|
||||
} else {
|
||||
alert(errCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
splashWindow.on('close', function () {
|
||||
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);
|
||||
webPreferences: {
|
||||
nodeIntegrationInSubFrames: true,
|
||||
preload: path.join(__dirname, 'pgadmin_preload.js'),
|
||||
},
|
||||
});
|
||||
|
||||
if (menu.name == 'file') {
|
||||
let runtimeMenu = getRuntimeMenu();
|
||||
_menu.append(runtimeMenu);
|
||||
}
|
||||
splashWindow.close();
|
||||
pgAdminMainScreen.webContents.session.clearCache();
|
||||
|
||||
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.label == item.label ? m.checked = true : m.checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
item.callback();
|
||||
setupMenu(pgAdminMainScreen, {
|
||||
'view_logs': ()=>{
|
||||
const win = new BrowserWindow({
|
||||
show: false,
|
||||
width: 800,
|
||||
height: 460,
|
||||
position: 'center',
|
||||
resizable: false,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'other_preload.js'),
|
||||
},
|
||||
}));
|
||||
});
|
||||
}
|
||||
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);
|
||||
});
|
||||
win.loadFile('./src/html/view_log.html');
|
||||
win.once('ready-to-show', ()=>{
|
||||
win.show();
|
||||
});
|
||||
},
|
||||
'configure': openConfigure,
|
||||
});
|
||||
|
||||
pgAdminMainScreen.loadURL(startPageUrl);
|
||||
pgAdminMainScreen.setBounds(configStore.get('bounds'));
|
||||
pgAdminMainScreen.show();
|
||||
|
||||
pgAdminMainScreen.webContents.setWindowOpenHandler(({url})=>{
|
||||
let openDocsInBrowser = configStore.get('openDocsInBrowser', true);
|
||||
let isDocURL = false;
|
||||
docsURLSubStrings.forEach(function (key) {
|
||||
if (url.indexOf(key) >= 0) {
|
||||
isDocURL = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (openDocsInBrowser && isDocURL) {
|
||||
// Do not open the window
|
||||
shell.openExternal(url);
|
||||
return { action: 'deny' };
|
||||
} else {
|
||||
return {
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: {
|
||||
'position': 'center',
|
||||
'min_width': 640,
|
||||
'min_height': 480,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
...pgAdminMainScreen.getBounds(),
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'pgadmin_preload.js'),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
pgAdminMainScreen.on('closed', ()=>{
|
||||
misc.cleanupAndQuitApp();
|
||||
});
|
||||
|
||||
pgAdminMainScreen.on('close', () => {
|
||||
configStore.set('bounds', pgAdminMainScreen.getBounds());
|
||||
pgAdminMainScreen.removeAllListeners('close');
|
||||
pgAdminMainScreen.close();
|
||||
});
|
||||
}
|
||||
|
||||
let splashWindow;
|
||||
|
||||
// setup preload events.
|
||||
ipcMain.handle('showOpenDialog', (e, options) => dialog.showOpenDialog(BrowserWindow.fromWebContents(e.sender), options));
|
||||
ipcMain.handle('showSaveDialog', (e, options) => dialog.showSaveDialog(BrowserWindow.fromWebContents(e.sender), options));
|
||||
ipcMain.handle('showMessageBox', (e, options) => dialog.showMessageBox(BrowserWindow.fromWebContents(e.sender), 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.on('restartApp', ()=>{
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
});
|
||||
ipcMain.handle('log', (text) => ()=>{
|
||||
misc.writeServerLog(text);
|
||||
});
|
||||
ipcMain.handle('checkPortAvailable', async (_e, fixedPort)=>{
|
||||
try {
|
||||
await misc.getAvailablePort(fixedPort);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
ipcMain.handle('openConfigure', openConfigure);
|
||||
|
||||
app.whenReady().then(() => {
|
||||
splashWindow = new BrowserWindow({
|
||||
transparent: true,
|
||||
width: 750,
|
||||
height: 600,
|
||||
frame: false,
|
||||
movable: true,
|
||||
focusable: true,
|
||||
resizable: false,
|
||||
show: false,
|
||||
icon: '../../assets/pgAdmin4.png',
|
||||
});
|
||||
|
||||
splashWindow.loadFile('./src/html/splash.html');
|
||||
splashWindow.center();
|
||||
|
||||
splashWindow.on('show', function () {
|
||||
let fixedPortCheck = configStore.get('fixedPort', false);
|
||||
if (fixedPortCheck) {
|
||||
serverPort = configStore.get('portNo');
|
||||
//Start the pgAdmin in Desktop mode.
|
||||
startDesktopMode();
|
||||
} else {
|
||||
// get the available TCP port by sending port no to 0.
|
||||
misc.getAvailablePort(0)
|
||||
.then((pythonApplicationPort) => {
|
||||
serverPort = pythonApplicationPort;
|
||||
//Start the pgAdmin in Desktop mode.
|
||||
startDesktopMode();
|
||||
})
|
||||
.catch((errCode) => {
|
||||
if (errCode === 'EADDRINUSE') {
|
||||
dialog.showErrorBox('Error', 'The port specified is already in use. Please enter a free port number.');
|
||||
} else {
|
||||
dialog.showErrorBox('Error', errCode.toString());
|
||||
}
|
||||
splashWindow.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
splashWindow.show();
|
||||
});
|
||||
@@ -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';
|
||||
});
|
||||
});
|
||||
@@ -199,9 +199,9 @@ def dump_header():
|
||||
def dump_runtime():
|
||||
print_title("Runtime Dependencies")
|
||||
print_table_header()
|
||||
print_row("Python", "3.6+", "PSF", "https://www.python.org/")
|
||||
print_row("nw", "0.50.2", "MIT",
|
||||
"git://github.com/nwjs/npm-installer.git")
|
||||
print_row("Python", "3.8+", "PSF", "https://www.python.org/")
|
||||
print_row("Electron", "30.0.5", "MIT",
|
||||
"https://www.npmjs.com/package/electron")
|
||||
|
||||
# Make sure to change the count of hardcoded_deps if we will
|
||||
# manually add some more dependencies in future.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
requests>=2.21.0
|
||||
requests[security]>=2.21.0
|
||||
safety>=1.9.0
|
||||
Sphinx==7.3.7
|
||||
# Due to issue https://github.com/sphinx-doc/sphinx/issues/11739, we have pinned the Sphinx version to 6.1.3.
|
||||
Sphinx==6.1.3
|
||||
sphinxcontrib-youtube==1.4.1
|
||||
@@ -1,7 +0,0 @@
|
||||
generated
|
||||
node_modules
|
||||
vendor
|
||||
templates/
|
||||
templates\
|
||||
ycache
|
||||
regression/htmlcov
|
||||
@@ -6,98 +6,122 @@
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
const globals = require('globals');
|
||||
const js = require('@eslint/js');
|
||||
const reactjs = require('eslint-plugin-react');
|
||||
const jest = require('eslint-plugin-jest');
|
||||
const babel = require('@babel/eslint-plugin');
|
||||
const babelParser = require('@babel/eslint-parser');
|
||||
const ts = require('typescript-eslint');
|
||||
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'amd': true,
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: [
|
||||
'**/generated',
|
||||
'**/node_modules',
|
||||
'**/vendor',
|
||||
'**/templates/',
|
||||
'**/templates\\',
|
||||
'**/ycache',
|
||||
'**/regression/htmlcov',
|
||||
],
|
||||
},
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
/* Uncomment the below line to use linter error for React Hooks.
|
||||
By Default commented so that builds are generated successfully.
|
||||
Once all the errors will be resolved will uncomment it and commit it.
|
||||
*/
|
||||
// "plugin:react-hooks/recommended",
|
||||
],
|
||||
'parser': '@babel/eslint-parser',
|
||||
'parserOptions': {
|
||||
'requireConfigFile': false,
|
||||
'ecmaVersion': 2018,
|
||||
'ecmaFeatures': {
|
||||
'jsx': true,
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'],
|
||||
languageOptions: {
|
||||
'parser': babelParser,
|
||||
ecmaVersion: 2018,
|
||||
parserOptions: {
|
||||
'ecmaFeatures': {
|
||||
'jsx': true,
|
||||
},
|
||||
'requireConfigFile': false,
|
||||
'babelOptions': {
|
||||
'plugins': [
|
||||
'@babel/plugin-syntax-jsx',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
],
|
||||
},
|
||||
},
|
||||
'sourceType': 'module',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.es2017,
|
||||
...globals.amd,
|
||||
'_': 'readonly',
|
||||
'module': 'readonly',
|
||||
'__dirname': 'readonly',
|
||||
'global': 'readonly',
|
||||
'jest': 'readonly',
|
||||
'process': 'readonly',
|
||||
},
|
||||
},
|
||||
'sourceType': 'module',
|
||||
'babelOptions': {
|
||||
'plugins': [
|
||||
'@babel/plugin-syntax-jsx',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'plugins': {
|
||||
'react': reactjs,
|
||||
'@babel': babel,
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
2,
|
||||
],
|
||||
},
|
||||
},
|
||||
'plugins': [
|
||||
'react',
|
||||
'@babel',
|
||||
'jest'
|
||||
],
|
||||
'overrides': [
|
||||
{
|
||||
'files': ['**/*.{ts,tsx}'],
|
||||
'plugins': [
|
||||
'@typescript-eslint',
|
||||
'linebreak-style': 0,
|
||||
'quotes': [
|
||||
'error',
|
||||
'single',
|
||||
],
|
||||
'extends': ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/eslint-recommended'],
|
||||
'parser': '@typescript-eslint/parser',
|
||||
'rules': {
|
||||
'@typescript-eslint/no-explicit-any': ['off'],
|
||||
'@typescript-eslint/no-this-alias': ['off'],
|
||||
}
|
||||
'semi': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'only-multiline',
|
||||
],
|
||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
||||
// We need to exclude below for RegEx case
|
||||
'no-useless-escape': 'off',
|
||||
'no-prototype-builtins': 'off',
|
||||
'no-global-assign': 'off',
|
||||
'no-import-assign': 'off',
|
||||
'react/jsx-uses-vars': 'error',
|
||||
'react/jsx-uses-react': 'error'
|
||||
},
|
||||
{
|
||||
'files': ['**/*{spec,test}.{js,jsx}', './regression/javascript/**/*.{js}'],
|
||||
'extends': ['eslint:recommended'],
|
||||
'env': {
|
||||
'jest': true
|
||||
}
|
||||
},
|
||||
],
|
||||
'globals': {
|
||||
'_': true,
|
||||
'module': true,
|
||||
'__dirname': true,
|
||||
'global': true,
|
||||
'jest': true
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
2,
|
||||
],
|
||||
'linebreak-style': 0,
|
||||
'quotes': [
|
||||
'error',
|
||||
'single',
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'only-multiline',
|
||||
],
|
||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
||||
// We need to exclude below for RegEx case
|
||||
'no-useless-escape': 0,
|
||||
'no-prototype-builtins': 0,
|
||||
'no-global-assign': 0,
|
||||
'no-import-assign': 0,
|
||||
},
|
||||
'settings': {
|
||||
'react': {
|
||||
'version': 'detect',
|
||||
'settings': {
|
||||
'react': {
|
||||
'version': 'detect',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
{
|
||||
'files': ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
'parser': ts.parser,
|
||||
},
|
||||
'plugins': {
|
||||
'@typescript-eslint': ts.plugin,
|
||||
},
|
||||
'rules': {
|
||||
'no-unused-vars': 'off',
|
||||
'no-undef': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['error'],
|
||||
'@typescript-eslint/no-explicit-any': ['off'],
|
||||
'@typescript-eslint/no-this-alias': ['off'],
|
||||
}
|
||||
},
|
||||
{
|
||||
'files': ['**/*{spec,test}.{js,jsx}', './regression/javascript/**/*.{js}'],
|
||||
...jest.configs['flat/recommended'],
|
||||
rules: {
|
||||
...jest.configs['flat/recommended'].rules,
|
||||
'jest/prefer-expect-assertions': 'off',
|
||||
'jest/expect-expect': 'off',
|
||||
'jest/no-identical-title': 'off',
|
||||
'jest/no-done-callback': 'off',
|
||||
'jest/no-conditional-expect': 'off',
|
||||
'jest/valid-title': 'off',
|
||||
},
|
||||
}
|
||||
];
|
||||
@@ -461,12 +461,14 @@ DEFAULT_BINARY_PATHS = {
|
||||
"pg-14": "",
|
||||
"pg-15": "",
|
||||
"pg-16": "",
|
||||
"pg-17": "",
|
||||
"ppas": "",
|
||||
"ppas-12": "",
|
||||
"ppas-13": "",
|
||||
"ppas-14": "",
|
||||
"ppas-15": "",
|
||||
"ppas-16": ""
|
||||
"ppas-16": "",
|
||||
"ppas-17": ""
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
@@ -481,12 +483,14 @@ FIXED_BINARY_PATHS = {
|
||||
"pg-14": "",
|
||||
"pg-15": "",
|
||||
"pg-16": "",
|
||||
"pg-17": "",
|
||||
"ppas": "",
|
||||
"ppas-12": "",
|
||||
"ppas-13": "",
|
||||
"ppas-14": "",
|
||||
"ppas-15": "",
|
||||
"ppas-16": ""
|
||||
"ppas-16": "",
|
||||
"ppas-17": ""
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
@@ -925,6 +929,12 @@ SERVER_HEARTBEAT_TIMEOUT = 30 # In seconds
|
||||
#############################################################################
|
||||
ENABLE_SERVER_PASS_EXEC_CMD = False
|
||||
|
||||
#############################################################################
|
||||
# Number of records to fetch in one batch for server logs.
|
||||
##############################################################################
|
||||
|
||||
ON_DEMAND_LOG_COUNT = 10000
|
||||
|
||||
#############################################################################
|
||||
# Patch the default config with custom config and other manipulations
|
||||
#############################################################################
|
||||
|
||||
@@ -8,8 +8,8 @@ Create Date: 2024-05-17 19:35:03.700104
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm.session import Session
|
||||
from pgadmin.model import Preferences
|
||||
from pgadmin.model import db
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ac2c2e27dc2d'
|
||||
@@ -19,9 +19,11 @@ depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
db.session.query(Preferences).filter(
|
||||
session = Session(bind=op.get_bind())
|
||||
|
||||
session.query(Preferences).filter(
|
||||
Preferences.name == 'execute_query').update({'name': 'execute_script'})
|
||||
db.session.commit()
|
||||
session.commit()
|
||||
|
||||
meta = sa.MetaData()
|
||||
meta.reflect(op.get_bind(), only=('user_macros',))
|
||||
|
||||
@@ -8,141 +8,138 @@
|
||||
"license": "PostgreSQL",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.2",
|
||||
"@babel/eslint-parser": "^7.21.3",
|
||||
"@babel/eslint-plugin": "^7.17.7",
|
||||
"@babel/eslint-parser": "^7.24.7",
|
||||
"@babel/eslint-plugin": "^7.24.7",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.10.1",
|
||||
"@babel/plugin-syntax-jsx": "^7.16.0",
|
||||
"@babel/plugin-transform-runtime": "^7.22.15",
|
||||
"@babel/preset-env": "^7.10.2",
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@emotion/core": "^10.0.14",
|
||||
"@babel/preset-typescript": "^7.24.7",
|
||||
"@emotion/memoize": "^0.7.5",
|
||||
"@emotion/react": "^11.11.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@emotion/utils": "^1.0.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@testing-library/jest-dom": "^6.1.2",
|
||||
"@testing-library/react": "12",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@testing-library/dom": "10.2.0",
|
||||
"@testing-library/jest-dom": "^6.4.6",
|
||||
"@testing-library/react": "16.0.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/jest": "^29.5.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.9",
|
||||
"@typescript-eslint/parser": "^5.57.0",
|
||||
"autoprefixer": "^10.2.4",
|
||||
"axios-mock-adapter": "^1.17.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"browserify": "^17.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^6.7.2",
|
||||
"css-minimizer-webpack-plugin": "^5.0.0",
|
||||
"eslint": "^8.37.0",
|
||||
"eslint": "^9.8.0",
|
||||
"eslint-plugin-jest": "^27.4.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"exports-loader": "^4.0.0",
|
||||
"globals": "^15.8.0",
|
||||
"html-react-parser": "^5.0.6",
|
||||
"image-minimizer-webpack-plugin": "^3.8.2",
|
||||
"image-minimizer-webpack-plugin": "^4.0.2",
|
||||
"imagemin": "^8.0.1",
|
||||
"imagemin-mozjpeg": "^10.0.0",
|
||||
"imagemin-optipng": "^8.0.0",
|
||||
"imports-loader": "^4.0.1",
|
||||
"is-docker": "^2.1.1",
|
||||
"jest": "^29.6.4",
|
||||
"jest-environment-jsdom": "^29.6.4",
|
||||
"loader-utils": "^3.2.1",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"postcss-loader": "^7.1.0",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"process": "^0.11.10",
|
||||
"prop-types": "^15.7.2",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"shim-loader": "^1.0.1",
|
||||
"style-loader": "^3.3.2",
|
||||
"stylis": "^4.0.7",
|
||||
"svgo": "^2.7.0",
|
||||
"svgo-loader": "^2.2.0",
|
||||
"svgo": "^3.3.2",
|
||||
"svgo-loader": "^4.0.0",
|
||||
"terser-webpack-plugin": "^5.1.1",
|
||||
"typescript": "^4.9.5",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript-eslint": "^7.15.0",
|
||||
"url-loader": "^4.1.1",
|
||||
"webfonts-loader": "^8.0.1",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-bundle-analyzer": "^4.8.0",
|
||||
"webpack-cli": "^4.5.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"yarn-audit-html": "4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/preset-react": "^7.12.13",
|
||||
"@codemirror/lang-sql": "^6.6.3",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-sql": "^6.6.5",
|
||||
"@date-io/core": "^3.0.0",
|
||||
"@date-io/date-fns": "3.x",
|
||||
"@emotion/sheet": "^1.0.1",
|
||||
"@fortawesome/fontawesome-free": "latest",
|
||||
"@mui/icons-material": "^5.15.10",
|
||||
"@mui/icons-material": "^5.16.6",
|
||||
"@mui/lab": "^5.0.0-alpha.165",
|
||||
"@mui/material": "^5.15.10",
|
||||
"@mui/styles": "^5.15.10",
|
||||
"@mui/x-date-pickers": "^6.19.7",
|
||||
"@projectstorm/react-diagrams": "^6.6.1",
|
||||
"@mui/x-date-pickers": "^7.7.1",
|
||||
"@projectstorm/react-diagrams": "^7.0.4",
|
||||
"@simonwep/pickr": "^1.5.1",
|
||||
"@szhsin/react-menu": "^2.2.0",
|
||||
"@tanstack/react-query": "5.37.1",
|
||||
"@tanstack/react-table": "^8.16.0",
|
||||
"@tanstack/react-virtual": "^3.4.0",
|
||||
"@tanstack/react-virtual": "^3.8.4",
|
||||
"@types/classnames": "^2.2.6",
|
||||
"@types/react": "^17.0.80",
|
||||
"@types/react-dom": "^17.0.25",
|
||||
"@types/react": "^18.0.2",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-search": "^0.15.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"ajv": "^8.8.2",
|
||||
"anti-trojan-source": "^1.4.0",
|
||||
"aspen-decorations": "^1.0.2",
|
||||
"axios": "^1.6.8",
|
||||
"axios": "^1.7.4",
|
||||
"babelify": "~10.0.0",
|
||||
"bignumber.js": "^9.0.1",
|
||||
"brace": "^0.11.1",
|
||||
"browserfs": "^1.4.3",
|
||||
"chart.js": "^3.0.0",
|
||||
"chartjs-plugin-zoom": "^2.0.1",
|
||||
"classnames": "^2.2.6",
|
||||
"closest": "^0.0.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"convert-units": "^2.3.4",
|
||||
"cssnano": "^5.0.2",
|
||||
"dagre": "^0.8.4",
|
||||
"date-fns": "^3.6.0",
|
||||
"diff-arrays-of-objects": "^1.1.8",
|
||||
"hotkeys-js": "^3.13.3",
|
||||
"html-to-image": "^1.11.11",
|
||||
"immutability-helper": "^3.0.0",
|
||||
"insert-if": "^1.1.0",
|
||||
"ip-address": "^7.1.0",
|
||||
"ip-address": "^9.0.5",
|
||||
"json-bignumber": "^1.0.1",
|
||||
"jsoneditor": "^9.5.4",
|
||||
"leaflet": "^1.5.1",
|
||||
"leaflet": "^1.9.4",
|
||||
"lodash": "4.*",
|
||||
"ml-matrix": "^6.5.0",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"notificar": "^1.0.1",
|
||||
"notistack": "^2.0.8",
|
||||
"notistack": "^3.0.1",
|
||||
"path-fx": "^2.0.0",
|
||||
"pathfinding": "^0.4.18",
|
||||
"paths-js": "^0.4.9",
|
||||
"postcss": "^8.4.31",
|
||||
"raf": "^3.4.1",
|
||||
"rc-dock": "^3.2.9",
|
||||
"react": "^17.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-arborist": "^3.2.0",
|
||||
"react-aspen": "^1.1.0",
|
||||
"react-checkbox-tree": "^1.7.2",
|
||||
"react-data-grid": "https://github.com/pgadmin-org/react-data-grid.git#200d2f5e02de694e3e9ffbe177c279bc40240fb8",
|
||||
"react-data-grid": "https://github.com/pgadmin-org/react-data-grid.git#4e10a5a327ff58198ac83c7b0c62549b20b78ae5",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-dropzone": "^14.2.1",
|
||||
"react-frame-component": "^5.2.6",
|
||||
"react-leaflet": "^3.2.2",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-new-window": "^1.0.1",
|
||||
"react-resize-detector": "^9.1.0",
|
||||
"react-resize-detector": "^11.0.1",
|
||||
"react-rnd": "^10.3.5",
|
||||
"react-select": "^5.7.2",
|
||||
"react-timer-hook": "^3.0.5",
|
||||
@@ -156,20 +153,16 @@
|
||||
"uplot-react": "^1.1.4",
|
||||
"valid-filename": "^2.0.1",
|
||||
"wkx": "^0.5.0",
|
||||
"xterm": "^4.11.0",
|
||||
"xterm-addon-fit": "^0.5.0",
|
||||
"xterm-addon-search": "^0.8.0",
|
||||
"xterm-addon-web-links": "^0.4.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"linter": "yarn eslint --no-eslintrc -c .eslintrc.js --ext .js --ext .jsx --ext .ts --ext .tsx .",
|
||||
"linter": "yarn run eslint -c .eslintrc.js .",
|
||||
"webpacker": "yarn run webpack --config webpack.config.js --progress",
|
||||
"webpacker:watch": "yarn run webpack --config webpack.config.js --progress --watch",
|
||||
"bundle:watch": "yarn run linter && yarn run webpacker:watch",
|
||||
"bundle:dev": "yarn run linter && yarn run webpacker",
|
||||
"bundle:analyze": "cross-env NODE_ENV=production ANALYZE=true yarn run bundle:dev",
|
||||
"bundle": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=3072 yarn run bundle:dev",
|
||||
"bundle": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=3072 yarn run bundle:dev && git log -1 --format=\"%H %as\" > commit_hash",
|
||||
"test:js-once": "yarn run linter && yarn run jest --maxWorkers=50%",
|
||||
"test:js": "yarn run test:js-once --watch",
|
||||
"test:js-file": "yarn run test:js-once -t",
|
||||
@@ -180,7 +173,10 @@
|
||||
"auditjs-html": "yarn audit --json | yarn run yarn-audit-html --output ../auditjs.html",
|
||||
"auditjs": "yarn audit --groups dependencies",
|
||||
"auditpy": "safety check --full-report -i 51668 -i 52495",
|
||||
"audit": "yarn run auditjs && yarn run auditpy"
|
||||
"audit-all": "yarn run auditjs && yarn run auditpy"
|
||||
},
|
||||
"packageManager": "yarn@3.6.4"
|
||||
"packageManager": "yarn@3.8.3",
|
||||
"browserslist": [
|
||||
"defaults and fully supports es6-module"
|
||||
]
|
||||
}
|
||||
@@ -15,8 +15,8 @@ import sys
|
||||
if sys.version_info <= (3, 9):
|
||||
import select
|
||||
|
||||
if sys.version_info < (3, 4):
|
||||
raise RuntimeError('This application must be run under Python 3.4 '
|
||||
if sys.version_info < (3, 8):
|
||||
raise RuntimeError('This application must be run under Python 3.8 '
|
||||
'or later.')
|
||||
import builtins
|
||||
import os
|
||||
@@ -103,6 +103,16 @@ if not os.path.isfile(config.SQLITE_PATH):
|
||||
app = create_app()
|
||||
app.config['sessions'] = dict()
|
||||
|
||||
# We load the file here instead of evaluate config
|
||||
# as we don't know the path of this file in evaluate config
|
||||
# commit_hash file resides in the web directory
|
||||
try:
|
||||
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'commit_hash')) as f:
|
||||
config.COMMIT_HASH = f.readline().strip()
|
||||
except FileNotFoundError as _:
|
||||
config.COMMIT_HASH = None
|
||||
|
||||
if setup_db_required:
|
||||
setup.setup_db(app)
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3, 4):
|
||||
raise Exception('This application must be run under Python 3.4 or later.')
|
||||
if sys.version_info < (3, 8):
|
||||
raise Exception('This application must be run under Python 3.8 or later.')
|
||||
|
||||
import builtins
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import ipaddress
|
||||
def get_my_ip():
|
||||
""" Return the public IP of this host """
|
||||
http = urllib3.PoolManager()
|
||||
IP_ADDRESS_STRING = '{}/{}'
|
||||
try:
|
||||
external_ip = http.request('GET', 'https://ident.me').data
|
||||
except Exception:
|
||||
@@ -29,11 +30,11 @@ def get_my_ip():
|
||||
|
||||
ip = ipaddress.ip_address(external_ip)
|
||||
if isinstance(ip, ipaddress.IPv4Address):
|
||||
return '{}/{}'.format(external_ip, 32)
|
||||
return IP_ADDRESS_STRING.format(external_ip, 32)
|
||||
elif isinstance(ip, ipaddress.IPv6Address):
|
||||
return '{}/{}'.format(external_ip, 128)
|
||||
return IP_ADDRESS_STRING.format(external_ip, 128)
|
||||
|
||||
return '{}/{}'.format(external_ip, 32)
|
||||
return IP_ADDRESS_STRING.format(external_ip, 32)
|
||||
|
||||
|
||||
def get_random_id():
|
||||
|
||||
@@ -520,13 +520,9 @@ def create_app(app_name=None):
|
||||
|
||||
security.init_app(app, user_datastore)
|
||||
|
||||
# register custom unauthorised handler.
|
||||
if sys.version_info < (3, 8):
|
||||
app.login_manager.unauthorized_handler(pga_unauthorised)
|
||||
else:
|
||||
# Flask-Security-Too > 5.4.* requires custom unauth handeler
|
||||
# to be registeres with it.
|
||||
security.unauthn_handler(pga_unauthorised)
|
||||
# Flask-Security-Too > 5.4.* requires custom unauth handeler
|
||||
# to be registeres with it.
|
||||
security.unauthn_handler(pga_unauthorised)
|
||||
|
||||
# Set the permanent session lifetime to the specified value in config file.
|
||||
app.permanent_session_lifetime = timedelta(
|
||||
|
||||
@@ -18,10 +18,10 @@ from pgadmin.utils.menu import MenuItem
|
||||
from pgadmin.utils.constants import MIMETYPE_APP_JS
|
||||
from pgadmin.utils.ajax import make_json_response
|
||||
import config
|
||||
import httpagentparser
|
||||
from pgadmin.model import User
|
||||
from user_agents import parse
|
||||
import platform
|
||||
import re
|
||||
|
||||
MODULE_NAME = 'about'
|
||||
|
||||
@@ -59,17 +59,18 @@ def index():
|
||||
"""Render the about box."""
|
||||
info = {}
|
||||
# Get OS , NW.js, Browser details
|
||||
browser, os_details, nwjs_version = detect_browser(request)
|
||||
browser, os_details, electron_version = detect_browser(request)
|
||||
admin = is_admin(current_user.email)
|
||||
|
||||
if nwjs_version:
|
||||
info['nwjs'] = nwjs_version
|
||||
if electron_version:
|
||||
info['electron'] = electron_version
|
||||
|
||||
if config.SERVER_MODE:
|
||||
info['app_mode'] = gettext('Server')
|
||||
else:
|
||||
info['app_mode'] = gettext('Desktop')
|
||||
|
||||
info['commit_hash'] = getattr(config, 'COMMIT_HASH', None)
|
||||
info['browser_details'] = browser
|
||||
info['version'] = config.APP_VERSION
|
||||
info['admin'] = admin
|
||||
@@ -118,32 +119,29 @@ def is_admin(load_user):
|
||||
|
||||
def detect_browser(request):
|
||||
"""This function returns the browser and os details"""
|
||||
nwjs_version = None
|
||||
electron_version = None
|
||||
agent = request.environ.get('HTTP_USER_AGENT')
|
||||
os_details = parse(platform.platform()).ua_string
|
||||
|
||||
if 'Nwjs' in agent:
|
||||
agent = agent.split('-')
|
||||
nwjs_version = agent[0].split(':')[1]
|
||||
browser = 'Chromium' + ' ' + agent[2]
|
||||
try:
|
||||
# available only for python 3.10 and above
|
||||
os_release = platform.freedesktop_os_release()
|
||||
os_details = os_release.get('PRETTY_NAME', '')
|
||||
if os_details:
|
||||
os_details += ', '
|
||||
except Exception as _:
|
||||
os_details = ''
|
||||
|
||||
os_details += parse(platform.platform()).ua_string
|
||||
|
||||
if 'Electron' in agent:
|
||||
electron_version = re.findall('Electron/([\\d.]+\\d+)', agent)[0]
|
||||
|
||||
browser = re.findall(
|
||||
'(opera|chrome|safari|firefox|msie|trident(?=/))/?\\s*([\\d.]+\\d+)',
|
||||
agent, re.IGNORECASE)
|
||||
if not browser:
|
||||
browser = agent.split('/')[0]
|
||||
else:
|
||||
browser = httpagentparser.detect(agent)
|
||||
if not browser:
|
||||
browser = agent.split('/')[0]
|
||||
else:
|
||||
browser = browser['browser']['name'] + ' ' + browser['browser'][
|
||||
'version']
|
||||
browser = " ".join(browser[0])
|
||||
|
||||
return browser, os_details, nwjs_version
|
||||
|
||||
|
||||
@blueprint.route("/about.js")
|
||||
@pga_login_required
|
||||
def script():
|
||||
"""render the required javascript"""
|
||||
return Response(
|
||||
response=render_template("about/about.js", _=gettext),
|
||||
status=200,
|
||||
mimetype=MIMETYPE_APP_JS
|
||||
)
|
||||
return browser, os_details, electron_version
|
||||
@@ -11,38 +11,13 @@ import gettext from 'sources/gettext';
|
||||
import url_for from 'sources/url_for';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Box, Grid, InputLabel } from '@mui/material';
|
||||
import { DefaultButton } from '../../../static/js/components/Buttons';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { InputText } from '../../../static/js/components/FormComponents';
|
||||
import { InputSQL } from '../../../static/js/components/FormComponents';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import { copyToClipboard } from '../../../static/js/clipboard';
|
||||
import { useDelayedCaller } from '../../../static/js/custom_hooks';
|
||||
import { usePgAdmin } from '../../../static/js/BrowserComponent';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
container: {
|
||||
padding: '16px',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
copyBtn: {
|
||||
marginRight: '1px',
|
||||
float: 'right',
|
||||
borderColor: theme.otherVars.borderColor,
|
||||
fontSize: '13px',
|
||||
},
|
||||
}));
|
||||
|
||||
export default function AboutComponent() {
|
||||
const classes = useStyles();
|
||||
const containerRef = useRef();
|
||||
const [aboutData, setAboutData] = useState([]);
|
||||
const [copyText, setCopyText] = useState(gettext('Copy'));
|
||||
const revertCopiedText = useDelayedCaller(()=>{
|
||||
setCopyText(gettext('Copy'));
|
||||
});
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -57,7 +32,7 @@ export default function AboutComponent() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box className={classes.container} ref={containerRef}>
|
||||
<Box sx={{ padding: '16px', height: '100%', display: 'flex',flexDirection: 'column'}} ref={containerRef}>
|
||||
<Grid container spacing={0} style={{marginBottom: '8px'}}>
|
||||
<Grid item lg={3} md={3} sm={3} xs={12}>
|
||||
<InputLabel style={{fontWeight: 'bold'}}>{gettext('Version')}</InputLabel>
|
||||
@@ -74,6 +49,14 @@ export default function AboutComponent() {
|
||||
<InputLabel>{aboutData.app_mode}</InputLabel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container spacing={0} style={{marginBottom: '8px'}}>
|
||||
<Grid item lg={3} md={3} sm={3} xs={12}>
|
||||
<InputLabel style={{fontWeight: 'bold'}}>{gettext('Commit:')}</InputLabel>
|
||||
</Grid>
|
||||
<Grid item lg={9} md={9} sm={9} xs={12}>
|
||||
<InputLabel>{aboutData.commit_hash}</InputLabel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container spacing={0} style={{marginBottom: '8px'}}>
|
||||
<Grid item lg={3} md={3} sm={3} xs={12}>
|
||||
<InputLabel style={{fontWeight: 'bold'}}>{gettext('Current User')}</InputLabel>
|
||||
@@ -82,13 +65,13 @@ export default function AboutComponent() {
|
||||
<InputLabel>{aboutData.current_user}</InputLabel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{ aboutData.nwjs &&
|
||||
{ aboutData.electron &&
|
||||
<Grid container spacing={0} style={{marginBottom: '8px'}}>
|
||||
<Grid item lg={3} md={3} sm={3} xs={12}>
|
||||
<InputLabel style={{fontWeight: 'bold'}}>{gettext('NW.js Version')}</InputLabel>
|
||||
<InputLabel style={{fontWeight: 'bold'}}>{gettext('Electron Version')}</InputLabel>
|
||||
</Grid>
|
||||
<Grid item lg={9} md={9} sm={9} xs={12}>
|
||||
<InputLabel>{aboutData.nwjs}</InputLabel>
|
||||
<InputLabel>{aboutData.electron}</InputLabel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
}
|
||||
@@ -131,18 +114,20 @@ export default function AboutComponent() {
|
||||
</Grid>
|
||||
}
|
||||
{ aboutData.settings &&
|
||||
<Box flexGrow="1" display="flex" flexDirection="column">
|
||||
<Box flexGrow="1" display="flex" flexDirection="column" minHeight="0">
|
||||
<Box>
|
||||
<span style={{fontWeight: 'bold'}}>{gettext('Server Configuration')}</span>
|
||||
<DefaultButton className={classes.copyBtn} onClick={()=>{
|
||||
copyToClipboard(aboutData.settings);
|
||||
setCopyText(gettext('Copied!'));
|
||||
revertCopiedText(1500);
|
||||
}}>{copyText}</DefaultButton>
|
||||
<InputLabel style={{fontWeight: 'bold'}}>{gettext('pgAdmin Server Configuration')}</InputLabel>
|
||||
</Box>
|
||||
<Box flexGrow="1" paddingTop="1px">
|
||||
<InputText style={{height: '100%'}} controlProps={{multiline: true, rows: 8}} inputStyle={{resize: 'none'}}
|
||||
value={aboutData.settings}/>
|
||||
<Box flexGrow="1" paddingTop="1px" minHeight="0">
|
||||
<InputSQL value={aboutData.settings}
|
||||
controlProps={{
|
||||
readonly: true,
|
||||
showCopyBtn: true,
|
||||
}}
|
||||
options={{
|
||||
lineNumbers: false,
|
||||
foldGutter: false
|
||||
}} />
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class BaseAuthentication(metaclass=AuthSourceRegistry):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def authenticate(self):
|
||||
def authenticate(self, form):
|
||||
pass
|
||||
|
||||
def validate(self, form):
|
||||
|
||||
@@ -27,7 +27,6 @@ from pgadmin.utils.constants import KERBEROS, MessageType
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.ajax import make_json_response, internal_server_error
|
||||
|
||||
|
||||
from pgadmin.authenticate.internal import BaseAuthentication
|
||||
from pgadmin.authenticate import get_auth_sources
|
||||
from pgadmin.utils.csrf import pgCSRFProtect
|
||||
|
||||
@@ -275,6 +275,6 @@ class OAuth2Authentication(BaseAuthentication):
|
||||
authorized_claims = [authorized_claims]
|
||||
if any(item in authorized_claims for item in claim):
|
||||
reason = "Claim match found. Authorized access."
|
||||
return (True, reason)
|
||||
reason = f"No match was found."
|
||||
return (False, reason)
|
||||
return True, reason
|
||||
reason = "No match was found."
|
||||
return False, reason
|
||||
@@ -65,13 +65,7 @@ from pgadmin.authenticate import AuthSourceManager
|
||||
from pgadmin.utils.exception import CryptKeyMissing
|
||||
|
||||
from pgadmin.user_login_check import pga_login_required
|
||||
|
||||
try:
|
||||
from flask_security.views import default_render_json
|
||||
except ImportError as e:
|
||||
# Support Flask-Security-Too == 3.2
|
||||
if sys.version_info < (3, 8):
|
||||
from flask_security.views import _render_json as default_render_json
|
||||
from flask_security.views import default_render_json
|
||||
|
||||
MODULE_NAME = 'browser'
|
||||
BROWSER_STATIC = 'browser.static'
|
||||
@@ -481,10 +475,6 @@ def utils():
|
||||
pg_help_path_pref = prefs.preference('pg_help_path')
|
||||
pg_help_path = pg_help_path_pref.get()
|
||||
|
||||
# Added to have theme value available at app start page loading
|
||||
prefs = Preferences.module('misc')
|
||||
theme = prefs.preference('theme').get()
|
||||
|
||||
# Get sqleditor options
|
||||
prefs = Preferences.module('sqleditor')
|
||||
|
||||
@@ -500,6 +490,11 @@ def utils():
|
||||
brace_matching_pref = prefs.preference('brace_matching')
|
||||
brace_matching = brace_matching_pref.get()
|
||||
|
||||
highlight_selection_matches_pref = prefs.preference(
|
||||
'highlight_selection_matches'
|
||||
)
|
||||
highlight_selection_matches = highlight_selection_matches_pref.get()
|
||||
|
||||
insert_pair_brackets_perf = prefs.preference('insert_pair_brackets')
|
||||
insert_pair_brackets = insert_pair_brackets_perf.get()
|
||||
|
||||
@@ -546,13 +541,13 @@ def utils():
|
||||
render_template(
|
||||
'browser/js/utils.js',
|
||||
layout=layout,
|
||||
theme=theme,
|
||||
jssnippets=snippets,
|
||||
pg_help_path=pg_help_path,
|
||||
editor_tab_size=editor_tab_size,
|
||||
editor_use_spaces=editor_use_spaces,
|
||||
editor_wrap_code=editor_wrap_code,
|
||||
editor_brace_matching=brace_matching,
|
||||
editor_highlight_selection_matches=highlight_selection_matches,
|
||||
editor_insert_pair_brackets=insert_pair_brackets,
|
||||
editor_indent_with_tabs=editor_indent_with_tabs,
|
||||
app_name=config.APP_NAME,
|
||||
@@ -1108,8 +1103,7 @@ if hasattr(config, 'SECURITY_RECOVERABLE') and config.SECURITY_RECOVERABLE:
|
||||
form_class = _security.forms.get('reset_password_form').cls
|
||||
form = form_class(request.form) if request.form else form_class()
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
form.user = user
|
||||
form.user = user
|
||||
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
|
||||
@@ -156,6 +156,21 @@ def register_browser_preferences(self):
|
||||
fields=fields
|
||||
)
|
||||
|
||||
self.preference.register(
|
||||
'keyboard_shortcuts',
|
||||
'close_tab_panel',
|
||||
gettext('Close tab panel'),
|
||||
'keyboardshortcut',
|
||||
{
|
||||
'alt': False,
|
||||
'shift': True,
|
||||
'control': True,
|
||||
'key': {'key_code': 87, 'char': 'w'}
|
||||
},
|
||||
category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
|
||||
fields=fields
|
||||
)
|
||||
|
||||
self.preference.register(
|
||||
'keyboard_shortcuts',
|
||||
'tabbed_panel_forward',
|
||||
@@ -428,7 +443,7 @@ def register_browser_preferences(self):
|
||||
)
|
||||
)
|
||||
|
||||
ope_new_tab_options = [
|
||||
open_new_tab_options = [
|
||||
{'label': gettext('Query Tool'), 'value': 'qt'},
|
||||
{'label': gettext('Debugger'), 'value': 'debugger'},
|
||||
{'label': gettext('Schema Diff'), 'value': 'schema_diff'},
|
||||
@@ -436,14 +451,14 @@ def register_browser_preferences(self):
|
||||
|
||||
# Allow psq tool to open in new browser tab if ENABLE_PSQL is set to True
|
||||
if config.ENABLE_PSQL:
|
||||
ope_new_tab_options.append(
|
||||
open_new_tab_options.append(
|
||||
{'label': gettext('PSQL Tool'), 'value': 'psql_tool'})
|
||||
|
||||
self.open_in_new_tab = self.preference.register(
|
||||
'tab_settings', 'new_browser_tab_open',
|
||||
gettext("Open in new browser tab"), 'select', None,
|
||||
category_label=PREF_LABEL_OPTIONS,
|
||||
options=ope_new_tab_options,
|
||||
options=open_new_tab_options,
|
||||
help_str=gettext(
|
||||
'Select Query Tool, Debugger, Schema Diff, ERD Tool '
|
||||
'or PSQL Tool from the drop-down to set '
|
||||
|
||||
@@ -165,6 +165,7 @@ class ServerModule(sg.ServerGroupPluginModule):
|
||||
server.tunnel_username = sharedserver.tunnel_username
|
||||
server.tunnel_password = sharedserver.tunnel_password
|
||||
server.save_password = sharedserver.save_password
|
||||
server.tunnel_identity_file = sharedserver.tunnel_identity_file
|
||||
if hasattr(server, 'connection_params') and \
|
||||
hasattr(sharedserver, 'connection_params') and \
|
||||
'passfile' in server.connection_params and \
|
||||
@@ -1615,6 +1616,11 @@ class ServerNode(PGChildNodeView):
|
||||
KEY_RING_SERVICE_NAME,
|
||||
KEY_RING_USERNAME_FORMAT.format(server.name,
|
||||
server.id))
|
||||
prompt_password = (
|
||||
True
|
||||
if password is None and server.passexec_cmd is None
|
||||
else False
|
||||
)
|
||||
else:
|
||||
password = data['password'] if 'password' in data else None
|
||||
save_password = data['save_password']\
|
||||
|
||||
@@ -35,6 +35,7 @@ from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
|
||||
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
|
||||
from pgadmin.model import db, Server, Database
|
||||
from pgadmin.browser.utils import underscore_escape
|
||||
from pgadmin.utils.constants import TWO_PARAM_STRING
|
||||
|
||||
|
||||
class DatabaseModule(CollectionNodeModule):
|
||||
@@ -586,7 +587,7 @@ class DatabaseView(PGChildNodeView):
|
||||
'icon': 'pg-icon-database',
|
||||
'already_connected': already_connected,
|
||||
'connected': True,
|
||||
'info_prefix': '{0}/{1}'.
|
||||
'info_prefix': TWO_PARAM_STRING.
|
||||
format(Server.query.filter_by(id=sid)[0].name, conn.db)
|
||||
}
|
||||
)
|
||||
@@ -609,7 +610,7 @@ class DatabaseView(PGChildNodeView):
|
||||
data={
|
||||
'icon': 'icon-database-not-connected',
|
||||
'connected': False,
|
||||
'info_prefix': '{0}/{1}'.
|
||||
'info_prefix': TWO_PARAM_STRING.
|
||||
format(Server.query.filter_by(id=sid)[0].name, conn.db)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -91,7 +91,7 @@ define('pgadmin.node.cast', [
|
||||
resolve(data);
|
||||
})
|
||||
.catch((err)=>{
|
||||
reject(err);
|
||||
reject(err instanceof Error ? err : Error(gettext('Something went wrong')));
|
||||
});
|
||||
} else {
|
||||
data = [];
|
||||
|
||||
@@ -207,7 +207,10 @@ export default class PublicationSchema extends BaseUISchema {
|
||||
state.only_table = false;
|
||||
return true;
|
||||
}
|
||||
if (!_.isUndefined(table) && table.length > 0 && !_.isEqual(this._origData.pubtable, state.pubtable)){
|
||||
if (
|
||||
!_.isUndefined(table) && table.length > 0 &&
|
||||
!_.isEqual(this.origData.pubtable, state.pubtable)
|
||||
){
|
||||
return false;
|
||||
}
|
||||
state.only_table = false;
|
||||
@@ -304,4 +307,4 @@ export default class PublicationSchema extends BaseUISchema {
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,4 @@ FROM
|
||||
pg_attribute
|
||||
WHERE
|
||||
attrelid = '{{ tid }}' :: regclass
|
||||
and attstattarget =-1;
|
||||
and attnum > 0;
|
||||
@@ -4,4 +4,4 @@ FROM
|
||||
pg_attribute
|
||||
WHERE
|
||||
attrelid = '{{ tid }}' :: regclass
|
||||
and attstattarget =-1;
|
||||
and attnum > 0;
|
||||
@@ -165,9 +165,8 @@ export default class CollationSchema extends BaseUISchema {
|
||||
else {
|
||||
if (actionObj.oldState.is_deterministic) {
|
||||
return { is_deterministic: false };
|
||||
} else {
|
||||
return { is_deterministic: true };
|
||||
}
|
||||
return { is_deterministic: true };
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
SELECT c.oid, c.collname AS name, COALESCE(c.collcollate, c.colllocale) AS lc_collate,
|
||||
COALESCE(c.collctype, c.colllocale) AS lc_type, c.collisdeterministic AS is_deterministic, c.collversion AS version,
|
||||
c.collprovider AS provider, c.collicurules AS rules,
|
||||
pg_catalog.pg_get_userbyid(c.collowner) AS owner, description, n.nspname AS schema
|
||||
FROM pg_catalog.pg_collation c
|
||||
JOIN pg_catalog.pg_namespace n ON n.oid=c.collnamespace
|
||||
LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=c.oid AND des.classoid='pg_collation'::regclass)
|
||||
WHERE c.collnamespace = {{scid}}::oid
|
||||
{% if coid %} AND c.oid = {{coid}}::oid {% endif %}
|
||||
ORDER BY c.collname;
|
||||
@@ -51,7 +51,7 @@ export default class DomainConstraintSchema extends BaseUISchema {
|
||||
cell:'boolean', group: gettext('Definition'), min_version: 90200,
|
||||
mode: ['properties', 'create', 'edit'],
|
||||
readonly: function(state) {
|
||||
return !obj.isNew(state) && obj._origData.convalidated;
|
||||
return !obj.isNew(state) && obj.origData.convalidated;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -24,7 +24,7 @@ ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
|
||||
{{ VARIABLE.SET(conn, 'COLUMN', data.name, data.attoptions) }}
|
||||
{% endif %}
|
||||
{### Alter column statistics value ###}
|
||||
{% if data.attstattarget is defined and data.attstattarget > -1 %}
|
||||
{% if data.attstattarget is defined and data.attstattarget is not none and data.attstattarget > -1 %}
|
||||
|
||||
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
|
||||
ALTER COLUMN {{conn|qtTypeIdent(data.name)}} SET STATISTICS {{data.attstattarget}};
|
||||
|
||||
@@ -93,7 +93,7 @@ ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.basensp, data.name)}}
|
||||
|
||||
{% endif %}
|
||||
{### Alter column statistics value ###}
|
||||
{% if c.attstattarget is defined and c.attstattarget > -1 %}
|
||||
{% if c.attstattarget is defined and c.attstattarget is not none and c.attstattarget > -1 %}
|
||||
ALTER FOREIGN TABLE IF EXISTS {{conn|qtIdent(data.basensp, data.name)}}
|
||||
ALTER COLUMN {{conn|qtTypeIdent(c.name)}} SET STATISTICS {{c.attstattarget}};
|
||||
|
||||
|
||||
@@ -825,7 +825,7 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare):
|
||||
if sql is None:
|
||||
return None
|
||||
start = 0
|
||||
start_position = re.search("\\s+[is|as]+\\s+", sql, flags=re.I)
|
||||
start_position = re.search("\\s+(is|as)+\\s+", sql, flags=re.I)
|
||||
|
||||
if start_position:
|
||||
start = start_position.start() + 4
|
||||
|
||||
@@ -582,7 +582,7 @@ class EdbFuncView(PGChildNodeView, DataTypeReader):
|
||||
if sql is None:
|
||||
return None
|
||||
start = 0
|
||||
start_position = re.search(r"\s+[is|as]+\s+", sql, flags=re.I)
|
||||
start_position = re.search(r"\s+(is|as)+\s+", sql, flags=re.I)
|
||||
|
||||
if start_position:
|
||||
start = start_position.start() + 4
|
||||
|
||||
@@ -100,7 +100,10 @@ export default class PackageSchema extends BaseUISchema {
|
||||
group: gettext('Header'),
|
||||
depChange: (state, source, topState, actionObj) => {
|
||||
|
||||
if(packageSchemaObj._origData.oid && state.pkgheadsrc != actionObj.oldState.pkgheadsrc) {
|
||||
if(
|
||||
packageSchemaObj.origData.oid &&
|
||||
state.pkgheadsrc != actionObj.oldState.pkgheadsrc
|
||||
) {
|
||||
packageSchemaObj.warningText = gettext(
|
||||
'Updating the package header definition may remove its existing body.'
|
||||
) + '<br><br><b>' + gettext('Do you want to continue?') +
|
||||
@@ -116,7 +119,10 @@ export default class PackageSchema extends BaseUISchema {
|
||||
mode: ['properties', 'create', 'edit'], group: gettext('Body'),
|
||||
depChange: (state, source, topState, actionObj) => {
|
||||
|
||||
if(packageSchemaObj._origData.oid && state.pkgbodysrc != actionObj.oldState.pkgbodysrc) {
|
||||
if(
|
||||
packageSchemaObj.origData.oid &&
|
||||
state.pkgbodysrc != actionObj.oldState.pkgbodysrc
|
||||
) {
|
||||
packageSchemaObj.warningText = gettext(
|
||||
'Updating the package header definition may remove its existing body.'
|
||||
) + '<br><br><b>' + gettext('Do you want to continue?') +
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import * as Node from 'pgbrowser/node';
|
||||
import * as SchemaTreeNode from './schema_child_tree_node';
|
||||
|
||||
let SchemaChildNode = Node.extend({
|
||||
const SchemaChildNode = Node.extend({
|
||||
parent_type: ['schema', 'catalog'],
|
||||
canDrop: SchemaTreeNode.isTreeItemOfChildOfSchema,
|
||||
canDropCascade: SchemaTreeNode.isTreeItemOfChildOfSchema,
|
||||
|
||||
@@ -587,7 +587,7 @@ export default class ColumnSchema extends BaseUISchema {
|
||||
}
|
||||
|
||||
validate(state, setError) {
|
||||
let msg = undefined;
|
||||
let msg;
|
||||
|
||||
if (!_.isUndefined(state.cltype) && !isEmptyString(state.attlen)) {
|
||||
// Validation for Length field
|
||||
|
||||
@@ -128,10 +128,7 @@ export default class CompoundTriggerSchema extends BaseUISchema {
|
||||
}
|
||||
// Enable column only if update event is set true
|
||||
let isUpdate = state.evnt_update;
|
||||
if(!_.isUndefined(isUpdate) && isUpdate) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return !(!_.isUndefined(isUpdate) && isUpdate);
|
||||
},
|
||||
readonly: function(state) { return !obj.isNew(state); },
|
||||
},{
|
||||
|
||||
@@ -24,6 +24,8 @@ from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
|
||||
constraints.exclusion_constraint import utils as exclusion_utils
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
|
||||
import check_pgstattuple
|
||||
|
||||
|
||||
class ExclusionConstraintModule(ConstraintTypeModule):
|
||||
@@ -837,13 +839,7 @@ class ExclusionConstraintView(PGChildNodeView):
|
||||
Returns the statistics for a particular object if cid is specified
|
||||
"""
|
||||
|
||||
# Check if pgstattuple extension is already created?
|
||||
# if created then only add extended stats
|
||||
status, is_pgstattuple = self.conn.execute_scalar("""
|
||||
SELECT (pg_catalog.count(extname) > 0) AS is_pgstattuple
|
||||
FROM pg_catalog.pg_extension
|
||||
WHERE extname='pgstattuple'
|
||||
""")
|
||||
status, is_pgstattuple = check_pgstattuple(self.conn, tid)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=is_pgstattuple)
|
||||
|
||||
|
||||
@@ -68,7 +68,6 @@ class ExclusionColHeaderSchema extends BaseUISchema {
|
||||
return this.exColumnSchema.getNewData({
|
||||
is_exp: data.is_exp,
|
||||
column: data.is_exp ? data.expression : data.column,
|
||||
column_cid: data.is_exp ? null : column?.cid,
|
||||
col_type: data.is_exp ? null : column?.datatype,
|
||||
});
|
||||
}
|
||||
@@ -379,13 +378,13 @@ export default class ExclusionConstraintSchema extends BaseUISchema {
|
||||
if(obj.inTable && source[0] == 'columns') {
|
||||
if(actionObj.type == SCHEMA_STATE_ACTIONS.DELETE_ROW) {
|
||||
let column = _.get(actionObj.oldState, actionObj.path.concat(actionObj.value));
|
||||
currColumns = _.filter(currColumns, (cc)=>cc.column_cid != column.cid);
|
||||
currColumns = _.filter(currColumns, (cc)=>cc.column != column.name);
|
||||
} else if(actionObj.type == SCHEMA_STATE_ACTIONS.SET_VALUE) {
|
||||
let tabColPath = _.slice(actionObj.path, 0, -1);
|
||||
let column = _.get(topState, tabColPath);
|
||||
let idx = _.findIndex(currColumns, (cc)=>cc.column_cid == column.cid);
|
||||
let tabColPath =_.slice(actionObj.path, 0, -1);
|
||||
let column = _.get(actionObj.oldState, tabColPath);
|
||||
let idx = _.findIndex(currColumns, (cc)=>cc.column == column.name);
|
||||
if(idx > -1) {
|
||||
currColumns[idx].column = column.name;
|
||||
currColumns[idx].column = _.get(topState, tabColPath).name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,6 @@ class ForeignKeyHeaderSchema extends BaseUISchema {
|
||||
let references_table_name = _.find(this.refTables, (t)=>t.value==data.references || t.value == this.origData.references)?.label;
|
||||
return {
|
||||
local_column: data.local_column,
|
||||
local_column_cid: _.find(this.fieldOptions.local_column, (c)=>c.value == data.local_column)?.cid,
|
||||
referenced: data.referenced,
|
||||
references: data.references,
|
||||
references_table_name: references_table_name,
|
||||
@@ -348,11 +347,11 @@ export default class ForeignKeySchema extends BaseUISchema {
|
||||
if(obj.inTable && source[0] == 'columns') {
|
||||
if(actionObj.type == SCHEMA_STATE_ACTIONS.DELETE_ROW) {
|
||||
let column = _.get(actionObj.oldState, actionObj.path.concat(actionObj.value));
|
||||
currColumns = _.filter(currColumns, (cc)=>cc.local_column_cid != column.cid);
|
||||
currColumns = _.filter(currColumns, (cc)=>cc.local_column != column.name);
|
||||
} else if(actionObj.type == SCHEMA_STATE_ACTIONS.SET_VALUE) {
|
||||
let tabColPath = _.slice(actionObj.path, 0, -1);
|
||||
let column = _.get(actionObj.oldState, tabColPath);
|
||||
let idx = _.findIndex(currColumns, (cc)=>cc.local_column_cid == column.cid);
|
||||
let idx = _.findIndex(currColumns, (cc)=>cc.local_column == column.name);
|
||||
if(idx > -1) {
|
||||
currColumns[idx].local_column = _.get(topState, tabColPath).name;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
|
||||
constraints.index_constraint import utils as idxcons_utils
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
|
||||
import check_pgstattuple
|
||||
|
||||
|
||||
class IndexConstraintModule(ConstraintTypeModule):
|
||||
@@ -926,13 +928,7 @@ class IndexConstraintView(PGChildNodeView):
|
||||
Returns the statistics for a particular object if cid is specified
|
||||
"""
|
||||
|
||||
# Check if pgstattuple extension is already created?
|
||||
# if created then only add extended stats
|
||||
status, is_pgstattuple = self.conn.execute_scalar("""
|
||||
SELECT (pg_catalog.count(extname) > 0) AS is_pgstattuple
|
||||
FROM pg_catalog.pg_extension
|
||||
WHERE extname='pgstattuple'
|
||||
""")
|
||||
status, is_pgstattuple = check_pgstattuple(self.conn, tid)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=is_pgstattuple)
|
||||
|
||||
|
||||
@@ -86,13 +86,14 @@ export default class PrimaryKeySchema extends BaseUISchema {
|
||||
if(obj.inTable && source[0] == 'columns') {
|
||||
if(actionObj.type == SCHEMA_STATE_ACTIONS.DELETE_ROW) {
|
||||
let oldColumn = _.get(actionObj.oldState, actionObj.path.concat(actionObj.value));
|
||||
currColumns = _.filter(currColumns, (cc)=>cc.cid != oldColumn.cid);
|
||||
currColumns = _.filter(currColumns, (cc)=>cc.column != oldColumn.name);
|
||||
} else if(actionObj.type == SCHEMA_STATE_ACTIONS.SET_VALUE) {
|
||||
let tabColPath = _.slice(actionObj.path, 0, -1);
|
||||
let oldCol = _.get(actionObj.oldState, tabColPath);
|
||||
let idx = _.findIndex(currColumns, (cc)=>cc.cid == oldCol.cid);
|
||||
if(idx > -1) {
|
||||
currColumns[idx].column = _.get(topState, tabColPath).name;
|
||||
let idx = _.findIndex(currColumns, (cc)=>cc.column == oldCol.name);
|
||||
let updatedCol = _.get(topState, tabColPath);
|
||||
if(idx > -1 && updatedCol.is_primary_key) {
|
||||
currColumns[idx].column = updatedCol.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,11 +88,11 @@ export default class UniqueConstraintSchema extends BaseUISchema {
|
||||
if(obj.inTable && source[0] == 'columns') {
|
||||
if(actionObj.type == SCHEMA_STATE_ACTIONS.DELETE_ROW) {
|
||||
let oldColumn = _.get(actionObj.oldState, actionObj.path.concat(actionObj.value));
|
||||
currColumns = _.filter(currColumns, (cc)=>cc.cid != oldColumn.cid);
|
||||
currColumns = _.filter(currColumns, (cc)=>cc.column != oldColumn.name);
|
||||
} else if(actionObj.type == SCHEMA_STATE_ACTIONS.SET_VALUE) {
|
||||
let tabColPath = _.slice(actionObj.path, 0, -1);
|
||||
let oldCol = _.get(actionObj.oldState, tabColPath);
|
||||
let idx = _.findIndex(currColumns, (cc)=>cc.cid == oldCol.cid);
|
||||
let idx = _.findIndex(currColumns, (cc)=>cc.column == oldCol.name);
|
||||
if(idx > -1) {
|
||||
currColumns[idx].column = _.get(topState, tabColPath).name;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ from pgadmin.tools.schema_diff.directory_compare import directory_diff
|
||||
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas. \
|
||||
tables.indexes import utils as index_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
|
||||
import check_pgstattuple
|
||||
|
||||
|
||||
class IndexesModule(CollectionNodeModule):
|
||||
@@ -998,14 +1000,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
|
||||
|
||||
if idx is not None:
|
||||
# Individual index
|
||||
|
||||
# Check if pgstattuple extension is already created?
|
||||
# if created then only add extended stats
|
||||
status, is_pgstattuple = self.conn.execute_scalar("""
|
||||
SELECT (pg_catalog.count(extname) > 0) AS is_pgstattuple
|
||||
FROM pg_catalog.pg_extension
|
||||
WHERE extname='pgstattuple'
|
||||
""")
|
||||
status, is_pgstattuple = check_pgstattuple(self.conn, tid)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=is_pgstattuple)
|
||||
|
||||
|
||||