Implemented runtime using NWjs to open pgAdmin4 in a standalone window
instead of the system tray and web browser. Used NWjs to get rid of QT and C++. Fixes #5967 Use cheroot as the default production server for pgAdmin4. Fixes #5017
3
.gitignore
vendored
@ -19,11 +19,12 @@
|
||||
/redhat-build
|
||||
/src-build
|
||||
/win-build
|
||||
/win-temp
|
||||
_build
|
||||
/build-*
|
||||
pgadmin4.log
|
||||
pkg/win32/installer.iss
|
||||
pkg/mac/codesign.conf
|
||||
pkg/mac/codesign.confx
|
||||
pkg/mac/framework.conf
|
||||
runtime/.qmake.cache
|
||||
runtime/.qmake.stash
|
||||
|
931
DEPENDENCIES
206
Make.bat
@ -3,6 +3,7 @@ SETLOCAL
|
||||
|
||||
SET WD=%CD%
|
||||
SET "BUILDROOT=%WD%\win-build"
|
||||
SET "TMPDIR=%WD%\win-temp"
|
||||
SET "DISTROOT=%WD%\dist"
|
||||
|
||||
SET CMDOPTIONS=""
|
||||
@ -18,19 +19,13 @@ IF "%1" == "clean" (
|
||||
EXIT /B %ERRORLEVEL%
|
||||
)
|
||||
|
||||
set "ARCHITECTURE=x64"
|
||||
if "%Platform%" == "X86" (
|
||||
set "ARCHITECTURE=x86"
|
||||
)
|
||||
|
||||
REM Main build sequence
|
||||
CALL :SET_ENVIRONMENT
|
||||
CALL :VALIDATE_ENVIRONMENT || EXIT /B 1
|
||||
CALL :CLEAN || EXIT /B 1
|
||||
CALL :CREATE_VIRTUAL_ENV || EXIT /B 1
|
||||
CALL :CREATE_RUNTIME_ENV || EXIT /B 1
|
||||
CALL :CREATE_PYTHON_ENV || EXIT /B 1
|
||||
CALL :CLEANUP_ENV || EXIT /B 1
|
||||
CALL :CREATE_RUNTIME_ENV || EXIT /B 1
|
||||
CALL :CREATE_INSTALLER || EXIT /B 1
|
||||
CALL :SIGN_INSTALLER || EXIT /B 1
|
||||
|
||||
@ -42,7 +37,10 @@ REM Main build sequence Ends
|
||||
ECHO Removing build directory...
|
||||
IF EXIST "%BUILDROOT%" RD "%BUILDROOT%" /S /Q > nul || EXIT /B 1
|
||||
|
||||
ECHO Removing temp build directory...
|
||||
ECHO Removing tmp directory...
|
||||
IF EXIST "%TMPDIR%" RD "%TMPDIR%" /S /Q > nul || EXIT /B 1
|
||||
|
||||
ECHO Removing installer build directory...
|
||||
IF EXIST "%WD%\pkg\win32\Output" rd "%WD%\pkg\win32\Output" /S /Q > nul || EXIT /B 1
|
||||
|
||||
ECHO Removing installer configuration script...
|
||||
@ -54,7 +52,6 @@ REM Main build sequence Ends
|
||||
:SET_ENVIRONMENT
|
||||
ECHO Configuring the environment...
|
||||
IF "%PGADMIN_PYTHON_DIR%" == "" SET "PGADMIN_PYTHON_DIR=C:\Python38"
|
||||
IF "%PGADMIN_QT_DIR%" == "" SET "PGADMIN_QT_DIR=C:\Qt\5.14.2\msvc2017_64"
|
||||
IF "%PGADMIN_KRB5_DIR%" == "" SET "PGADMIN_KRB5_DIR=C:\Program Files\MIT\Kerberos"
|
||||
IF "%PGADMIN_POSTGRES_DIR%" == "" SET "PGADMIN_POSTGRES_DIR=C:\Program Files (x86)\PostgreSQL\12"
|
||||
IF "%PGADMIN_INNOTOOL_DIR%" == "" SET "PGADMIN_INNOTOOL_DIR=C:\Program Files (x86)\Inno Setup 6"
|
||||
@ -62,7 +59,7 @@ REM Main build sequence Ends
|
||||
IF "%PGADMIN_SIGNTOOL_DIR%" == "" SET "PGADMIN_SIGNTOOL_DIR=C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64"
|
||||
|
||||
REM Set REDIST_NAME (the filename)
|
||||
set "VCREDIST_FILE=vcredist_%ARCHITECTURE%.exe"
|
||||
set "VCREDIST_FILE=vcredist_x64.exe"
|
||||
|
||||
REM Set additional variables we need
|
||||
FOR /F "tokens=3" %%a IN ('findstr /C:"APP_RELEASE =" %WD%\web\config.py') DO SET APP_MAJOR=%%a
|
||||
@ -74,13 +71,13 @@ REM Main build sequence Ends
|
||||
FOR /F "tokens=2* DELims='" %%a IN ('findstr /C:"APP_NAME =" web\config.py') DO SET APP_NAME=%%a
|
||||
FOR /f "tokens=1 DELims=." %%G IN ('%PGADMIN_PYTHON_DIR%/python.exe -c "print('%APP_NAME%'.lower().replace(' ', ''))"') DO SET APP_SHORTNAME=%%G
|
||||
SET APP_VERSION=%APP_MAJOR%.%APP_MINOR%
|
||||
SET INSTALLERNAME=%APP_SHORTNAME%-%APP_MAJOR%.%APP_MINOR%-%APP_VERSION_SUFFIX%-%ARCHITECTURE%.exe
|
||||
IF "%APP_VERSION_SUFFIX%" == "" SET INSTALLERNAME=%APP_SHORTNAME%-%APP_MAJOR%.%APP_MINOR%-%ARCHITECTURE%.exe
|
||||
SET INSTALLERNAME=%APP_SHORTNAME%-%APP_MAJOR%.%APP_MINOR%-%APP_VERSION_SUFFIX%-x64.exe
|
||||
IF "%APP_VERSION_SUFFIX%" == "" SET INSTALLERNAME=%APP_SHORTNAME%-%APP_MAJOR%.%APP_MINOR%-x64.exe
|
||||
|
||||
REM get Python version for the runtime build ex. 2.7.1 will be 27
|
||||
FOR /f "tokens=1 DELims=." %%G IN ('%PGADMIN_PYTHON_DIR%/python.exe -c "import sys; print(sys.version.split(' ')[0])"') DO SET PYTHON_MAJOR=%%G
|
||||
FOR /f "tokens=2 DELims=." %%G IN ('%PGADMIN_PYTHON_DIR%/python.exe -c "import sys; print(sys.version.split(' ')[0])"') DO SET PYTHON_MINOR=%%G
|
||||
SET "PYTHON_VERSION=%PYTHON_MAJOR%%PYTHON_MINOR%"
|
||||
FOR /f "tokens=3 DELims=." %%G IN ('%PGADMIN_PYTHON_DIR%/python.exe -c "import sys; print(sys.version.split(' ')[0])"') DO SET PYTHON_REVISION=%%G
|
||||
|
||||
EXIT /B 0
|
||||
|
||||
@ -94,10 +91,8 @@ REM Main build sequence Ends
|
||||
ECHO Installer name: %INSTALLERNAME%
|
||||
ECHO.
|
||||
ECHO Python directory: %PGADMIN_PYTHON_DIR%
|
||||
ECHO Python DLL: %PGADMIN_PYTHON_DIR%\Python%PYTHON_VERSION%.dll
|
||||
ECHO Python version: %PYTHON_MAJOR%.%PYTHON_MINOR%
|
||||
ECHO Python version: %PYTHON_MAJOR%.%PYTHON_MINOR%.%PYTHON_REVISION%
|
||||
ECHO.
|
||||
ECHO Qt directory: %PGADMIN_QT_DIR%
|
||||
ECHO KRB5 directory: %PGADMIN_KRB5_DIR%
|
||||
ECHO PostgreSQL directory: %PGADMIN_POSTGRES_DIR%
|
||||
ECHO.
|
||||
@ -125,36 +120,18 @@ REM Main build sequence Ends
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
IF NOT EXIST "%PGADMIN_QT_DIR%" (
|
||||
ECHO !PGADMIN_QT_DIR! does not exist.
|
||||
ECHO Please install Qt and set the PGADMIN_QT_DIR environment variable.
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
IF NOT EXIST "%PGADMIN_KRB5_DIR%" (
|
||||
ECHO !PGADMIN_KRB5_DIR! does not exist.
|
||||
ECHO Please install MIT Kerberos for Windows and set the PGADMIN_KRB5_DIR environment variable.
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
IF NOT EXIST "%PGADMIN_QT_DIR%\bin\qmake.exe" (
|
||||
ECHO !QMAKE! does not exist.
|
||||
ECHO Please install Qt and set the PGADMIN_QT_DIR environment variable.
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
IF NOT EXIST "%PGADMIN_PYTHON_DIR%" (
|
||||
ECHO !PGADMIN_PYTHON_DIR! does not exist.
|
||||
ECHO Please install Python and set the PGADMIN_PYTHON_DIR environment variable.
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
IF NOT EXIST "%PGADMIN_PYTHON_DIR%\Python%PYTHON_VERSION%.dll" (
|
||||
ECHO !PGADMIN_PYTHON_DIR!\Python!PYTHON_VERSION!.dll does not exist.
|
||||
ECHO Please check your Python installation is complete.
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
IF NOT EXIST "%PGADMIN_POSTGRES_DIR%" (
|
||||
ECHO !PGADMIN_POSTGRES_DIR! does not exist.
|
||||
ECHO Please install PostgreSQL and set the PGADMIN_POSTGRES_DIR environment variable.
|
||||
@ -174,47 +151,75 @@ REM Main build sequence Ends
|
||||
|
||||
:CREATE_VIRTUAL_ENV
|
||||
ECHO Creating virtual environment...
|
||||
IF NOT EXIST "%BUILDROOT%" MKDIR "%BUILDROOT%"
|
||||
|
||||
CD "%BUILDROOT%"
|
||||
IF NOT EXIST "%TMPDIR%" MKDIR "%TMPDIR%"
|
||||
|
||||
CD "%TMPDIR%"
|
||||
|
||||
REM Note that we must use virtualenv.exe here, as the venv module doesn't allow python.exe to relocate.
|
||||
"%PGADMIN_PYTHON_DIR%\Scripts\virtualenv.exe" venv
|
||||
|
||||
XCOPY /S /I /E /H /Y "%PGADMIN_PYTHON_DIR%\DLLs" "%BUILDROOT%\venv\DLLs" > nul || EXIT /B 1
|
||||
XCOPY /S /I /E /H /Y "%PGADMIN_PYTHON_DIR%\Lib" "%BUILDROOT%\venv\Lib" > nul || EXIT /B 1
|
||||
XCOPY /S /I /E /H /Y "%PGADMIN_PYTHON_DIR%\DLLs" "%TMPDIR%\venv\DLLs" > nul || EXIT /B 1
|
||||
XCOPY /S /I /E /H /Y "%PGADMIN_PYTHON_DIR%\Lib" "%TMPDIR%\venv\Lib" > nul || EXIT /B 1
|
||||
|
||||
ECHO Activating virtual environment - %BUILDROOT%\venv...
|
||||
CALL "%BUILDROOT%\venv\Scripts\activate" || EXIT /B 1
|
||||
ECHO Activating virtual environment - %TMPDIR%\venv...
|
||||
CALL "%TMPDIR%\venv\Scripts\activate" || EXIT /B 1
|
||||
|
||||
ECHO Installing dependencies...
|
||||
CALL pip install -r "%WD%\requirements.txt" || EXIT /B 1
|
||||
CALL pip install sphinx || EXIT /B 1
|
||||
|
||||
REM If this is Python 3.6+, we need to remove the hack above or it will break qmake. Sigh.
|
||||
IF %PYTHON_VERSION% GEQ 36 SET CL=
|
||||
CALL pip install --upgrade pip
|
||||
CALL pip install --only-binary=cryptography -r "%WD%\requirements.txt" || EXIT /B 1
|
||||
|
||||
CD %WD%
|
||||
EXIT /B 0
|
||||
|
||||
|
||||
:CREATE_PYTHON_ENV
|
||||
ECHO Staging Python...
|
||||
MKDIR "%BUILDROOT%\python\Lib" || EXIT /B 1
|
||||
|
||||
ECHO Downloading embedded Python...
|
||||
REM Get the python embeddable and extract it to %BUILDROOT%\python
|
||||
CD "%TMPDIR%
|
||||
%PGADMIN_PYTHON_DIR%\python -c "import sys; from urllib.request import urlretrieve; urlretrieve('https://www.python.org/ftp/python/' + sys.version.split(' ')[0] + '/python-' + sys.version.split(' ')[0] + '-embed-amd64.zip', 'python-embedded.zip')" || EXIT /B 1
|
||||
%PGADMIN_PYTHON_DIR%\python -c "import zipfile; z = zipfile.ZipFile('python-embedded.zip', 'r'); z.extractall('../win-build/python/')" || EXIT /B 1
|
||||
|
||||
ECHO Copying site-packages...
|
||||
XCOPY /S /I /E /H /Y "%TMPDIR%\venv\Lib\site-packages" "%BUILDROOT%\python\Lib\site-packages" > nul || EXIT /B 1
|
||||
|
||||
REM NOTE: There is intentionally no space after "site" in the line below, to prevent Python barfing if there's one in the file
|
||||
ECHO import site>> "%BUILDROOT%\python\python%PYTHON_MAJOR%%PYTHON_MINOR%._pth"
|
||||
|
||||
ECHO Staging Kerberos components...
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\kinit.exe" "%BUILDROOT%\python" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\krb5_64.dll" "%BUILDROOT%\python" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\comerr64.dll" "%BUILDROOT%\python" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\k5sprt64.dll" "%BUILDROOT%\python" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\gssapi64.dll" "%BUILDROOT%\python" > nul || EXIT /B 1
|
||||
|
||||
ECHO Cleaning up unnecessary .pyc and .pyo files...
|
||||
FOR /R "%BUILDROOT%\python" %%f in (*.pyc *.pyo) do DEL /q "%%f" 1> nul 2>&1
|
||||
ECHO Removing tests...
|
||||
FOR /R "%BUILDROOT%\python\Lib" %%f in (test tests) do RD /Q /S "%%f" 1> nul 2>&1
|
||||
|
||||
EXIT /B 0
|
||||
|
||||
|
||||
:CREATE_RUNTIME_ENV
|
||||
IF NOT EXIST "%BUILDROOT%" MKDIR "%BUILDROOT%"
|
||||
MKDIR "%BUILDROOT%\runtime"
|
||||
|
||||
CD "%WD%\web"
|
||||
|
||||
ECHO Installing javascript dependencies...
|
||||
CALL yarn install || EXIT /B 1
|
||||
|
||||
ECHO Bundling javascript...
|
||||
CALL yarn run bundle || EXIT /B 1
|
||||
|
||||
ECHO Removing webpack caches...
|
||||
RD /Q /S "%WD%\web\pgadmin\static\js\generated\.cache" 1> nul 2>&1
|
||||
|
||||
ECHO Copying web directory...
|
||||
XCOPY /S /I /E /H /Y "%WD%\web" "%BUILDROOT%\web" > nul || EXIT /B 1
|
||||
|
||||
ECHO Installing javascript dependencies...
|
||||
CD "%BUILDROOT%\web"
|
||||
CALL yarn install || EXIT /B 1
|
||||
|
||||
ECHO Bundling javascript...
|
||||
CALL yarn run bundle || EXIT /B 1
|
||||
|
||||
ECHO Cleaning up unnecessary .pyc and .pyo files...
|
||||
FOR /R "%BUILDROOT%\web" %%f in (*.pyc *.pyo) do DEL /q "%%f" 1> nul 2>&1
|
||||
ECHO Removing tests, Python caches and node modules...
|
||||
@ -240,60 +245,34 @@ REM Main build sequence Ends
|
||||
ECHO } >> "%BUILDROOT%\web\config_distro.py"
|
||||
|
||||
ECHO Building docs...
|
||||
CALL pip install sphinx || EXIT /B 1
|
||||
MKDIR "%BUILDROOT%\docs\en_US\html"
|
||||
CD "%WD%\docs\en_US"
|
||||
CALL "%BUILDROOT%\venv\Scripts\python.exe" build_code_snippet.py || EXIT /B 1
|
||||
CALL "%BUILDROOT%\venv\Scripts\sphinx-build.exe" "%WD%\docs\en_US" "%BUILDROOT%\docs\en_US\html" || EXIT /B 1
|
||||
CALL "%TMPDIR%\venv\Scripts\python.exe" build_code_snippet.py || EXIT /B 1
|
||||
CALL "%TMPDIR%\venv\Scripts\sphinx-build.exe" "%WD%\docs\en_US" "%BUILDROOT%\docs\en_US\html" || EXIT /B 1
|
||||
|
||||
ECHO Removing Sphinx
|
||||
CALL pip uninstall -y sphinx Pygments alabaster colorama docutils imagesize requests snowballstemmer
|
||||
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
|
||||
|
||||
ECHO Assembling runtime environment...
|
||||
CD "%WD%\runtime"
|
||||
COPY "%WD%\runtime\package.json" "%BUILDROOT%\runtime\" > nul || EXIT /B 1
|
||||
CD "%BUILDROOT%\runtime\"
|
||||
CALL yarn install --production=true || EXIT /B 1
|
||||
|
||||
ECHO Running qmake...
|
||||
CALL set "PGADMIN_PYTHON_DIR=%PGADMIN_PYTHON_DIR%" && "%PGADMIN_QT_DIR%\bin\qmake.exe" || EXIT /B 1
|
||||
ECHO Downloading NWjs to %TMPDIR%...
|
||||
CALL yarn --cwd "%TMPDIR%" add nw || EXIT /B
|
||||
|
||||
ECHO Cleaning the build directory...
|
||||
CALL nmake clean || EXIT /B 1
|
||||
XCOPY /S /I /E /H /Y "%TMPDIR%\node_modules\nw\nwjs\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
MOVE "%BUILDROOT%\runtime\nw.exe" "%BUILDROOT%\runtime\pgAdmin4.exe"
|
||||
|
||||
ECHO Running make...
|
||||
CALL nmake || EXIT /B 1
|
||||
|
||||
ECHO Staging pgAdmin4.exe...
|
||||
COPY "%WD%\runtime\release\pgAdmin4.exe" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
|
||||
ECHO Staging Qt components...
|
||||
COPY "%PGADMIN_QT_DIR%\bin\Qt5Core.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_QT_DIR%\bin\Qt5Gui.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_QT_DIR%\bin\Qt5Widgets.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_QT_DIR%\bin\Qt5Network.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_QT_DIR%\bin\Qt5Svg.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
MKDIR "%BUILDROOT%\runtime\platforms" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_QT_DIR%\plugins\platforms\qwindows.dll" "%BUILDROOT%\runtime\platforms" > nul || EXIT /B 1
|
||||
MKDIR "%BUILDROOT%\runtime\imageformats" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_QT_DIR%\plugins\imageformats\qsvg.dll" "%BUILDROOT%\runtime\imageformats" > nul || EXIT /B 1
|
||||
ECHO [Paths] > "%BUILDROOT%\runtime\qt.conf"
|
||||
ECHO Plugins=plugins >> "%BUILDROOT%\runtime\qt.conf"
|
||||
|
||||
ECHO Staging Kerberos components...
|
||||
IF "%ARCHITECTURE%" == "x64" (
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\kinit.exe" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\krb5_64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\comerr64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\k5sprt64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_KRB5_DIR%\bin\gssapi64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
)
|
||||
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
|
||||
IF "%ARCHITECTURE%" == "x64" (
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libcrypto-1_1-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libssl-1_1-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
) ELSE (
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libcrypto-1_1.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libssl-1_1.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
)
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libcrypto-1_1-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\libssl-1_1-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
IF EXIST "%PGADMIN_POSTGRES_DIR%\bin\libintl-*.dll" COPY "%PGADMIN_POSTGRES_DIR%\bin\libintl-*.dll" "%BUILDROOT%\runtime" > nul
|
||||
IF EXIST "%PGADMIN_POSTGRES_DIR%\bin\libiconv-*.dll" COPY "%PGADMIN_POSTGRES_DIR%\bin\libiconv-*.dll" "%BUILDROOT%\runtime" > nul
|
||||
COPY "%PGADMIN_POSTGRES_DIR%\bin\zlib.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
@ -308,22 +287,6 @@ REM Main build sequence Ends
|
||||
|
||||
CD %WD%
|
||||
EXIT /B 0
|
||||
|
||||
|
||||
:CREATE_PYTHON_ENV
|
||||
ECHO Staging Python...
|
||||
COPY %PGADMIN_PYTHON_DIR%\python%PYTHON_VERSION%.dll "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY %PGADMIN_PYTHON_DIR%\python.exe "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
COPY %PGADMIN_PYTHON_DIR%\pythonw.exe "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
||||
|
||||
ECHO Cleaning up unnecessary .pyc and .pyo files...
|
||||
FOR /R "%BUILDROOT%\venv" %%f in (*.pyc *.pyo) do DEL /q "%%f" 1> nul 2>&1
|
||||
ECHO Removing tests...
|
||||
FOR /R "%BUILDROOT%\venv\Lib" %%f in (test tests) do RD /Q /S "%%f" 1> nul 2>&1
|
||||
ECHO Removing TCL...
|
||||
RD /Q /S "%BUILDROOT%\venv\tcl" 1> nul 2>&1
|
||||
|
||||
EXIT /B 0
|
||||
|
||||
|
||||
:CREATE_INSTALLER
|
||||
@ -339,12 +302,7 @@ REM Main build sequence Ends
|
||||
CALL "%PGADMIN_PYTHON_DIR%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in" "-o" "%WD%\pkg\win32\installer.iss.in_stage1" "-s" MYAPP_NAME -r """%APP_NAME%"""
|
||||
CALL "%PGADMIN_PYTHON_DIR%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage1" "-o" "%WD%\pkg\win32\installer.iss.in_stage2" "-s" MYAPP_FULLVERSION -r """%APP_VERSION%"""
|
||||
CALL "%PGADMIN_PYTHON_DIR%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage2" "-o" "%WD%\pkg\win32\installer.iss.in_stage3" "-s" MYAPP_VERSION -r """v%APP_MAJOR%"""
|
||||
|
||||
SET ARCMODE=
|
||||
IF "%ARCHITECTURE%" == "x64" (
|
||||
set ARCMODE="x64"
|
||||
)
|
||||
CALL "%PGADMIN_PYTHON_DIR%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage3" "-o" "%WD%\pkg\win32\installer.iss.in_stage4" "-s" MYAPP_ARCHITECTURESMODE -r """%ARCMODE%"""
|
||||
CALL "%PGADMIN_PYTHON_DIR%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage3" "-o" "%WD%\pkg\win32\installer.iss.in_stage4" "-s" MYAPP_ARCHITECTURESMODE -r """x64"""
|
||||
CALL "%PGADMIN_PYTHON_DIR%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage4" "-o" "%WD%\pkg\win32\installer.iss" "-s" MYAPP_VCDIST -r """%PGADMIN_VCREDIST_DIRNAME%\%VCREDIST_FILE%"""
|
||||
|
||||
ECHO Cleaning up...
|
||||
@ -377,14 +335,6 @@ REM Main build sequence Ends
|
||||
EXIT /B 0
|
||||
|
||||
|
||||
:CLEANUP_ENV
|
||||
ECHO Cleaning the build environment...
|
||||
RD "%BUILDROOT%\venv\Include" /S /Q 1> nul 2>&1
|
||||
DEL /s "%BUILDROOT%\venv\pip-selfcheck.json" 1> nul 2>&1
|
||||
|
||||
EXIT /B 0
|
||||
|
||||
|
||||
:USAGE
|
||||
ECHO Invalid command line options.
|
||||
ECHO Usage: "Make.bat [clean]"
|
||||
|
2
Makefile
@ -20,7 +20,7 @@ APP_REVISION := $(shell grep ^APP_REVISION web/config.py | awk -F"=" '{print $$N
|
||||
# Include only platform-independent builds in all
|
||||
all: docs pip src runtime
|
||||
|
||||
appbundle: docs
|
||||
appbundle:
|
||||
./pkg/mac/build.sh
|
||||
|
||||
install-node:
|
||||
|
84
README
@ -17,59 +17,32 @@ utilised.
|
||||
|
||||
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 a QT based runtime application intended to allow this -
|
||||
it is essentially a Python application server that runs in the system tray
|
||||
and allows the user to connect to the application using their web browser.
|
||||
subdirectory contains an NWjs based runtime application intended to allow this,
|
||||
which will execute the Python server and display the UI.
|
||||
|
||||
Building the Runtime
|
||||
--------------------
|
||||
|
||||
To build the runtime, the following packages must be installed:
|
||||
|
||||
- QT 5 (Use the VC++ build on Windows, not MinGW).
|
||||
- Python 3.4+
|
||||
- NodeJS 12+
|
||||
- Yarn
|
||||
|
||||
An environment variable named PGADMIN_PYTHON_DIR must be set to the directory
|
||||
in which Python has been installed, for example:
|
||||
Change into the runtime directory, and run "yarn install". This will install the
|
||||
dependencies required.
|
||||
|
||||
- /usr
|
||||
- /usr/local/python-3.8
|
||||
- C:\Python38
|
||||
In order to use the runtime in a development environment, you'll need to copy
|
||||
dev_config.json.in file to dev_config.json, and edit the paths to the Python
|
||||
executable and pgAdmin.py file, otherwise the runtime will use the default
|
||||
paths it would expect to find in the standard package for your platform.
|
||||
|
||||
Assuming both qmake is in the path:
|
||||
You can then execute the runtime by running something like:
|
||||
|
||||
dpage@hal:~/git/pgadmin4$ cd runtime
|
||||
dpage@hal:~/git/pgadmin4/runtime$ export PGADMIN_PYTHON_DIR=/opt/local
|
||||
dpage@hal:~/git/pgadmin4/runtime$ qmake
|
||||
Project MESSAGE: ==================================
|
||||
Project MESSAGE: Configuring the pgAdmin 4 runtime.
|
||||
Project MESSAGE: ==================================
|
||||
Project MESSAGE: Qt version: 5
|
||||
Project MESSAGE: Platform: macOS
|
||||
Project MESSAGE: Python executable: /opt/local/bin/python3
|
||||
Project MESSAGE: Python version: 3.8 (38)
|
||||
Project MESSAGE: Python config: /opt/local/bin/python3-config
|
||||
Project MESSAGE: CXXFLAGS: -pipe -stdlib=libc++ -I/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8 -I/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8
|
||||
Project MESSAGE: LDFLAGS: -stdlib=libc++ -L/opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/config-3.8-darwin -ldl -framework CoreFoundation
|
||||
Project MESSAGE: LIBS: -lpython3.8 -ldl -framework CoreFoundation
|
||||
dpage@hal:~/git/pgadmin4/runtime$ make
|
||||
...
|
||||
node_modules/nw/nwjs/nw .
|
||||
|
||||
To build the runtime in debug mode, use the option below with qmake:
|
||||
$ qmake CONFIG+=debug
|
||||
or on macOS:
|
||||
|
||||
To build the runtime in release mode, use the option below with qmake:
|
||||
$ qmake CONFIG+=release
|
||||
|
||||
By default, the runtime application will be built in release mode.
|
||||
|
||||
On Linux, an executable called 'pgAdmin4' will be built, on Windows,
|
||||
'pgAdmin4.exe', and on Mac OS X, an app bundle called pgAdmin4.app will be
|
||||
created.
|
||||
|
||||
You can also use Qt Creator to build, develop and debug the runtime. Simply
|
||||
open the $PGADMIN4_SRC/runtime/pgAdmin4.pro project file in Qt Creator and
|
||||
configure the project with a supported version of Qt when prompted.
|
||||
node_modules/nw/nwjs/nwjs.app/Contents/MacOS/nwjs .
|
||||
|
||||
Create Database Migrations
|
||||
--------------------------
|
||||
@ -92,7 +65,7 @@ Configuring the Python Environment
|
||||
----------------------------------
|
||||
|
||||
In order to run the Python code, a suitable runtime environment is required.
|
||||
Python version 3.4 and later are currently supported. It is recommended that a
|
||||
Python version 3.5 and later are currently supported. It is recommended that a
|
||||
Python Virtual Environment is setup for this purpose, rather than using the
|
||||
system Python environment. On Linux and Mac systems, the process is fairly
|
||||
simple - adapt as required for your distribution:
|
||||
@ -231,33 +204,6 @@ entries to make them available in preferences.
|
||||
The name of the theme is derived from the directory name. Underscores (_) and
|
||||
hyphens (-) will be replaced with spaces and the result will be camel cased.
|
||||
|
||||
Configuring the Runtime
|
||||
-----------------------
|
||||
|
||||
The pgAdmin 4 Runtime maintains it's own Python Path to avoid conflicts with
|
||||
packages or other issues in the system Python installation. It will also search
|
||||
a number of known locations for the pgAdmin4.py file needed to run pgAdmin
|
||||
(including relative locations in a source code tree), however you can specify
|
||||
an alternate path if needed.
|
||||
|
||||
If either a working environment or pgAdmin4.py cannot be found at startup, the
|
||||
runtime will prompt for the locations. Alternatively, you can click the try
|
||||
icon and select the Configuration option to open the configuration dialogue.
|
||||
|
||||
On a Linux/Mac system, the Python Path will typically consist of a single path
|
||||
to the virtual environment's site-packages directory, e.g.
|
||||
|
||||
/Users/<USERNAME>/.virtualenvs/pgadmin4/lib/python3.8/site-packages
|
||||
|
||||
On Windows, multiple paths are likely to be required, e.g.
|
||||
|
||||
C:\Users\dpage\.virtualenvs\pgadmin4\Lib\site-packages;C:\Users\dpage\.virtualenvs\pgadmin4\Lib;C:\Users\dpage\.virtualenvs\pgadmin4\Lib\lib-tk;C:\Users\dpage\.virtualenvs\pgadmin4\DLLs
|
||||
|
||||
If you wish to specify a specific copy of the Python code to run, you can set
|
||||
the Application Path to a directory containing pgAdmin4.py, e.g.
|
||||
|
||||
/Users/<USERNAME>/git/pgadmin4-test/web/
|
||||
|
||||
Building the documentation
|
||||
--------------------------
|
||||
|
||||
|
@ -7,14 +7,14 @@
|
||||
The bulk of pgAdmin is a Python web application written using the Flask framework
|
||||
on the backend, and HTML5 with CSS3, Bootstrap and jQuery on the front end. A
|
||||
desktop runtime is also included for users that prefer a desktop application to
|
||||
a web application, which is written in C++ using the QT framework.
|
||||
a web application, which is written using NWjs (Node Webkit).
|
||||
|
||||
Runtime
|
||||
*******
|
||||
|
||||
The runtime is essentially a Python webserver and browser in a box. Found in the
|
||||
**/runtime** directory in the source tree, it is a relatively simple QT
|
||||
application that is most easily modified using the **QT Creator** application.
|
||||
The runtime is based on NWjs 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.
|
||||
|
||||
Web Application
|
||||
***************
|
||||
@ -213,4 +213,4 @@ divided each module in small chunks as much as possible. Not all javascript
|
||||
modules are required to be loaded (i.e. loading a javascript module for
|
||||
database will make sense only when a server node is loaded completely.) Please
|
||||
look at the the javascript files node.js, browser.js, menu.js, panel.js, etc for
|
||||
better understanding of the code.
|
||||
better understanding of the code.
|
||||
|
@ -109,54 +109,6 @@ names.
|
||||
.. note:: From version 3.0 onwards, new or refactored code should be written using
|
||||
ES6 features and conventions.
|
||||
|
||||
C++
|
||||
***
|
||||
|
||||
C++ code is used in the desktop runtime for the application, primarily with the
|
||||
QT framework and an embedded Python interpreter. Note the use of hanging braces,
|
||||
which may be omitted if on a single statement is present:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// Ping the application server to see if it's alive
|
||||
bool PingServer(QUrl url)
|
||||
{
|
||||
QNetworkAccessManager manager;
|
||||
QEventLoop loop;
|
||||
QNetworkReply *reply;
|
||||
QVariant redirectUrl;
|
||||
|
||||
url.setPath("/utils/ping");
|
||||
|
||||
do
|
||||
{
|
||||
reply = manager.get(QNetworkRequest(url));
|
||||
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
|
||||
redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
url = redirectUrl.toUrl();
|
||||
|
||||
if (!redirectUrl.isNull())
|
||||
delete reply;
|
||||
|
||||
} while (!redirectUrl.isNull());
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
return false;
|
||||
|
||||
QString response = reply->readAll();
|
||||
|
||||
if (response != "PING")
|
||||
{
|
||||
qDebug() << "Failed to connect, server response: " << response;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Python
|
||||
******
|
||||
|
||||
|
@ -8,13 +8,8 @@ pgAdmin may be deployed as a desktop application by configuring the application
|
||||
to run in desktop mode and then utilising the desktop runtime to host the
|
||||
program on a supported Windows, Mac OS X or Linux installation.
|
||||
|
||||
The desktop runtime is a system-tray application that when launched, runs the
|
||||
pgAdmin server and launches a web browser to render the user interface. If
|
||||
additional instances of pgAdmin are launched, a new browser tab will be opened
|
||||
and be served by the existing instance of the server in order to minimise system
|
||||
resource utilisation. Clicking the icon in the system tray will present a menu
|
||||
offering options to open a new pgAdmin window, configure the runtime, view the
|
||||
server log and shut down the server.
|
||||
The desktop runtime is a standalone application that when launched, runs the
|
||||
pgAdmin server and opens a window to render the user interface.
|
||||
|
||||
.. note:: Pre-compiled and configured installation packages are available for
|
||||
a number of platforms. These packages should be used by end-users whereever
|
||||
@ -42,19 +37,26 @@ Runtime
|
||||
*******
|
||||
|
||||
When executed, the runtime will automatically try to execute the pgAdmin Python
|
||||
application. If execution fails, it will prompt you to adjust the Python Path
|
||||
to include the directories containing the pgAdmin code as well as any additional
|
||||
Python dependencies. You can enter a list of paths by separating them with a
|
||||
semi-colon character, for example:
|
||||
application. If execution fails, it will prompt you with error message
|
||||
displaying a *Configure* button at the bottom. You can configure a fixed port
|
||||
number to avoid clashes of the default random port number with other
|
||||
applications and a connection timeout if desired.
|
||||
|
||||
.. code-block:: bash
|
||||
If the error is related to Python Path or pgAdmin Python file then you need to
|
||||
create a file named 'dev_config.json' and specify the following entries:
|
||||
|
||||
/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/;/Users/dpage/python-libs/
|
||||
{
|
||||
"pythonPath": <PATH OF THE PYTHON BINARY> For Example: "../../venv/bin/python3",
|
||||
"pgadminFile": <PATH OF THE pgAdmin4.py> For Example: "../web/pgAdmin4.py"
|
||||
}
|
||||
|
||||
The configuration settings are stored using the QSettings class in Qt, which
|
||||
will use an INI file on Unix systems (~/.config/pgadmin/pgadmin4.conf),
|
||||
a plist file on Mac OS X (~/Library/Preferences/org.pgadmin.pgadmin4.plist),
|
||||
and the registry on Windows (HKEY_CURRENT_USER\\Software\\pgadmin\\pgadmin4).
|
||||
Note that the dev_config.py file should only be required by developers who are
|
||||
working outside of a standard installation.
|
||||
|
||||
The configuration settings are stored in *runtime_config.json* file, which
|
||||
will be available on Unix systems (~/.local/share/pgadmin/),
|
||||
on Mac OS X (~/Library/Preferences/pgadmin),
|
||||
and on Windows (%APPDATA%/pgadmin).
|
||||
|
||||
The configuration settings:
|
||||
|
||||
@ -65,18 +67,10 @@ The configuration settings:
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Key | Type | Purpose |
|
||||
+==========================+====================+===============================================================+
|
||||
| ApplicationPath | String | The directory containing pgAdmin4.py |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| BrowserCommand | String | An alternate command to run instead of the default browser. |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| ConnectionTimeout | Integer | The number of seconds to wait for application server startup. |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| FixedPort | Boolean | Use a fixed network port number rather than a random one. |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| OpenTabAtStartup | Boolean | Open a browser tab at startup. |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| PortNumber | Integer | The port number to use, if using a fixed port. |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| PythonPath | String | The Python module search path |
|
||||
| ConnectionTimeout | Integer | The number of seconds to wait for application server startup. |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
|
||||
|
@ -9,10 +9,12 @@ This release contains a number of bug fixes and new features since the release o
|
||||
New features
|
||||
************
|
||||
|
||||
| `Issue #5967 <https://redmine.postgresql.org/issues/5967>`_ - Implemented runtime using NWjs to open pgAdmin4 in a standalone window instead of the system tray and web browser.
|
||||
|
||||
Housekeeping
|
||||
************
|
||||
|
||||
| `Issue #5017 <https://redmine.postgresql.org/issues/5017>`_ - Use cheroot as the default production server for pgAdmin4.
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
@ -25,8 +27,3 @@ Bug fixes
|
||||
| `Issue #6177 <https://redmine.postgresql.org/issues/6177>`_ - Fixed an issue while downloading ERD images in Safari and Firefox.
|
||||
| `Issue #6179 <https://redmine.postgresql.org/issues/6179>`_ - Fixed an issue where Generate SQL displayed twice in the ERD tool.
|
||||
| `Issue #6180 <https://redmine.postgresql.org/issues/6180>`_ - Updated missing documentation for the 'Download Image' option in ERD.
|
||||
|
||||
. Documentation missing for 'Download Image' option in ERD. Fixes #6180.
|
||||
2. Generate SQL displayed twice in ERD tool. Fixes #6179.
|
||||
3. Zooming out too far makes the diagram vanish entirely. Fixes #6164.
|
||||
4. Zoom to fit button only works if the diagram is larger than the canvas. Fixes #6163.
|
||||
|
@ -36,7 +36,7 @@ Package: ${APP_NAME}-server
|
||||
Version: ${APP_LONG_VERSION}
|
||||
Architecture: ${OS_ARCH}
|
||||
Depends: python3, libpq5 (>= 11.0), libgssapi-krb5-2
|
||||
Recommends: postgresql-client | postgresql-client-12 | postgresql-client-11 | postgresql-client-10
|
||||
Recommends: postgresql-client | postgresql-client-13 | postgresql-client-12 | postgresql-client-11 | postgresql-client-10
|
||||
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
|
||||
Description: The core server package for pgAdmin. 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.
|
||||
EOF
|
||||
@ -56,7 +56,7 @@ cat << EOF > "${DESKTOPROOT}/DEBIAN/control"
|
||||
Package: ${APP_NAME}-desktop
|
||||
Version: ${APP_LONG_VERSION}
|
||||
Architecture: ${OS_ARCH}
|
||||
Depends: ${APP_NAME}-server, libqt5gui5
|
||||
Depends: ${APP_NAME}-server
|
||||
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
|
||||
Description: The desktop user interface for pgAdmin. 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.
|
||||
EOF
|
||||
|
@ -30,5 +30,5 @@ apt update
|
||||
|
||||
# Install pre-reqs
|
||||
echo "Installing build pre-requisites..."
|
||||
apt install -y build-essential python3-dev python3-venv python3-sphinx python3-wheel libpq-dev libffi-dev qtbase5-dev qt5-qmake nodejs yarn libkrb5-dev
|
||||
apt install -y build-essential python3-dev python3-venv python3-sphinx python3-wheel libpq-dev libffi-dev nodejs yarn libkrb5-dev
|
||||
|
||||
|
@ -53,7 +53,8 @@ _create_python_virtualenv() {
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
# Make sure we have the wheel package present
|
||||
# Make sure we have the wheel package present, as well as the latest pip
|
||||
pip3 install --upgrade pip
|
||||
pip3 install wheel
|
||||
|
||||
# Install the requirements
|
||||
@ -63,7 +64,7 @@ _create_python_virtualenv() {
|
||||
# Use "python3" here as we want the venv path
|
||||
PYMODULES_PATH=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
|
||||
DIR_PYMODULES_PATH=`dirname ${PYMODULES_PATH}`
|
||||
|
||||
|
||||
# Use /usr/bin/python3 here as we want the system path
|
||||
if [ $1 == "debian" ]; then
|
||||
PYSYSLIB_PATH=$(/usr/bin/python3 -c "import sys; print('%s/lib/python%d.%.d' % (sys.prefix, sys.version_info.major, sys.version_info.minor))")
|
||||
@ -105,23 +106,60 @@ _create_python_virtualenv() {
|
||||
}
|
||||
|
||||
_build_runtime() {
|
||||
echo "Building the desktop runtime..."
|
||||
cd ${SOURCEDIR}/runtime
|
||||
if [ -f Makefile ]; then
|
||||
make clean
|
||||
fi
|
||||
if hash qmake-qt5 2>/dev/null; then
|
||||
PGADMIN_PYTHON_DIR=/usr qmake-qt5
|
||||
else
|
||||
PGADMIN_PYTHON_DIR=/usr qmake
|
||||
fi
|
||||
make
|
||||
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.
|
||||
|
||||
# YARN:
|
||||
# yarn add --cwd "${BUILDROOT}" nw
|
||||
# YARN END
|
||||
|
||||
# WGET:
|
||||
NW_VERSION=$(yarn info nw | grep latest | awk -F "'" '{ print $2}')
|
||||
pushd "${BUILDROOT}" > /dev/null
|
||||
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
|
||||
done
|
||||
tar -zxvf nwjs-v${NW_VERSION}-linux-x64.tar.gz
|
||||
popd > /dev/null
|
||||
# WGET END
|
||||
|
||||
# Copy nwjs into the staging directory
|
||||
mkdir -p "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
cp pgAdmin4 "${DESKTOPROOT}/usr/${APP_NAME}/bin/pgadmin4"
|
||||
mkdir -p "${DESKTOPROOT}/usr/${APP_NAME}/share"
|
||||
cp pgAdmin4.ico "${DESKTOPROOT}/usr/${APP_NAME}/share/pgadmin4.ico"
|
||||
|
||||
# YARN:
|
||||
# cp -r "${BUILDROOT}/node_modules/nw/nwjs"/* "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
# YARN END
|
||||
|
||||
# WGET:
|
||||
cp -r "${BUILDROOT}/nwjs-v${NW_VERSION}-linux-x64"/* "${DESKTOPROOT}/usr/${APP_NAME}/bin"
|
||||
# WGET END
|
||||
|
||||
mv "${DESKTOPROOT}/usr/${APP_NAME}/bin/nw" "${DESKTOPROOT}/usr/${APP_NAME}/bin/${APP_NAME}"
|
||||
|
||||
cp -r "${SOURCEDIR}/runtime/assets" "${DESKTOPROOT}/usr/${APP_NAME}/bin/assets"
|
||||
cp -r "${SOURCEDIR}/runtime/src" "${DESKTOPROOT}/usr/${APP_NAME}/bin/src"
|
||||
|
||||
cp "${SOURCEDIR}/runtime/package.json" "${DESKTOPROOT}/usr/${APP_NAME}/bin/"
|
||||
yarn --cwd "${DESKTOPROOT}/usr/${APP_NAME}/bin" install --production=true
|
||||
|
||||
# Create the icon
|
||||
mkdir -p "${DESKTOPROOT}/usr/share/icons/hicolor/128x128/apps/"
|
||||
cp "${SOURCEDIR}/pkg/linux/pgadmin4-128x128.png" "${DESKTOPROOT}/usr/share/icons/hicolor/128x128/apps/${APP_NAME}.png"
|
||||
mkdir -p "${DESKTOPROOT}/usr/share/icons/hicolor/64x64/apps/"
|
||||
cp "${SOURCEDIR}/pkg/linux/pgadmin4-64x64.png" "${DESKTOPROOT}/usr/share/icons/hicolor/64x64/apps/${APP_NAME}.png"
|
||||
mkdir -p "${DESKTOPROOT}/usr/share/icons/hicolor/48x48/apps/"
|
||||
cp "${SOURCEDIR}/pkg/linux/pgadmin4-48x48.png" "${DESKTOPROOT}/usr/share/icons/hicolor/48x48/apps/${APP_NAME}.png"
|
||||
mkdir -p "${DESKTOPROOT}/usr/share/icons/hicolor/32x32/apps/"
|
||||
cp "${SOURCEDIR}/pkg/linux/pgadmin4-32x32.png" "${DESKTOPROOT}/usr/share/icons/hicolor/32x32/apps/${APP_NAME}.png"
|
||||
mkdir -p "${DESKTOPROOT}/usr/share/icons/hicolor/16x16/apps/"
|
||||
cp "${SOURCEDIR}/pkg/linux/pgadmin4-16x16.png" "${DESKTOPROOT}/usr/share/icons/hicolor/16x16/apps/${APP_NAME}.png"
|
||||
|
||||
mkdir -p "${DESKTOPROOT}/usr/share/applications"
|
||||
cp ../pkg/linux/pgadmin4.desktop "${DESKTOPROOT}/usr/share/applications"
|
||||
cp "${SOURCEDIR}/pkg/linux/pgadmin4.desktop" "${DESKTOPROOT}/usr/share/applications"
|
||||
}
|
||||
|
||||
_build_docs() {
|
||||
|
BIN
pkg/linux/pgadmin4-128x128.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
pkg/linux/pgadmin4-16x16.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
pkg/linux/pgadmin4-32x32.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
pkg/linux/pgadmin4-48x48.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
pkg/linux/pgadmin4-64x64.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
@ -2,7 +2,7 @@
|
||||
Encoding=UTF-8
|
||||
Name=pgAdmin 4
|
||||
Exec=/usr/pgadmin4/bin/pgadmin4
|
||||
Icon=/usr/pgadmin4/share/pgadmin4.ico
|
||||
Icon=pgadmin4
|
||||
Type=Application
|
||||
Categories=Application;Development;
|
||||
MimeType=text/html
|
||||
|
2
pkg/mac/.gitignore
vendored
@ -1,4 +1,2 @@
|
||||
# Global excludes across all subdirectories
|
||||
codesign.conf
|
||||
debug.pgadmin.Info.plist
|
||||
pgadmin.Info.plist
|
||||
|
@ -1,22 +0,0 @@
|
||||
<?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>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>__SHORT_VERSION__</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>__FULL_VERSION__</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>Created by Qt/QMake</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>__FRAMEWORK_NAME__</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.pgadmin.__FRAMEWORK_NAME__</string>
|
||||
<key>NOTE</key>
|
||||
<string>Please, do NOT change this file -- It was generated by Qt/QMake.</string>
|
||||
</dict>
|
||||
</plist>
|
@ -1,22 +0,0 @@
|
||||
<?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>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>__SHORT_VERSION__</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>__FULL_VERSION__</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>Created by Qt/QMake</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>__FRAMEWORK_NAME__</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.qt-project.Qt.__FRAMEWORK_NAME__</string>
|
||||
<key>NOTE</key>
|
||||
<string>Please, do NOT change this file -- It was generated by Qt/QMake.</string>
|
||||
</dict>
|
||||
</plist>
|
73
pkg/mac/Info.plist.in
Normal file
@ -0,0 +1,73 @@
|
||||
<?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>BuildMachineOSBuild</key>
|
||||
<string>18G103</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>app.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>%APPID%</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>%APPVER%</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>%APPNAME%</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>4280.88</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>10.15</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx10.15</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1131</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>11C505</string>
|
||||
<key>GPUEjectPolicy</key>
|
||||
<string>wait</string>
|
||||
<key>LSEnvironment</key>
|
||||
<dict>
|
||||
<key>MallocNanoZone</key>
|
||||
<string>0</string>
|
||||
<key>SYSTEM_VERSION_COMPAT</key>
|
||||
<string>0</string>
|
||||
</dict>
|
||||
<key>LSFileQuarantineEnabled</key>
|
||||
<true/>
|
||||
<key>LSHasLocalizedDisplayName</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.10.0</string>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<true/>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>BrowserCrApplication</string>
|
||||
<key>NSRequiresAquaSystemAppearance</key>
|
||||
<false/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>NSUserActivityTypeBrowsingWeb</string>
|
||||
</array>
|
||||
<key>NSUserNotificationAlertStyle</key>
|
||||
<string>none</string>
|
||||
<key>OSAScriptingDefinition</key>
|
||||
<string>scripting.sdef</string>
|
||||
<key>SCMRevision</key>
|
||||
<string>62f83a7521ae1f32e563795732dff0c9da1b660d-refs/heads/master@{#812354}</string>
|
||||
</dict>
|
||||
</plist>
|
@ -1 +0,0 @@
|
||||
APPL????
|
@ -1,44 +1,31 @@
|
||||
Building pgAdmin4.dmg on Mac OS X
|
||||
=================================
|
||||
Building pgAdmin4.dmg on macOS
|
||||
==============================
|
||||
|
||||
Required Packages (Either build the sources or get them from macports or
|
||||
similar):
|
||||
|
||||
1. Python installation
|
||||
- Python 3.4+ or above from https://www.python.org/
|
||||
1. Yarn & NodeJS
|
||||
|
||||
2. QT installation
|
||||
- Qt 5 from http://www.qt.io/
|
||||
|
||||
3. PostgreSQL installation
|
||||
- PostgreSQL 9.5 or above from http://www.postgresql.org/
|
||||
2. PostgreSQL installation
|
||||
- PostgreSQL 12 or above from http://www.postgresql.org/
|
||||
|
||||
Building:
|
||||
|
||||
1. If a value different from the default of /usr/local/python is required, set
|
||||
the PGADMIN_PYTHON_DIR environment variable to the Python root installation
|
||||
directory, e.g.
|
||||
1. To bundle a different version of Python from the default of 3.9.0, set the
|
||||
PGADMIN_PYTHON_VERSION environment variable, e.g:
|
||||
|
||||
export PGADMIN_PYTHON_DIR=/opt/local
|
||||
export PGADMIN_PYTHON_VERSION=3.8.5
|
||||
|
||||
2. If a value different from the default of ~/Qt/5.13.2/clang_64, is required,
|
||||
set the PGADMIN_QT_DIR environment variable to the QT root installation
|
||||
directory, e.g.
|
||||
|
||||
export PGADMIN_QT_DIR=~/Qt/5.14.2/clang_64
|
||||
|
||||
3. If a value different from the default of /usr/local/pgsql is required, set
|
||||
the PGADMIN_POSTGRES_DIR environment variable to the PostgreSQL installation
|
||||
directory, e.g.
|
||||
2. If a path different from the default of /usr/local/pgsql for the PostgreSQL
|
||||
installation has been used, set the PGADMIN_POSTGRES_DIR environment variable
|
||||
appropriately, e.g:
|
||||
|
||||
export PGADMIN_POSTGRES_DIR=/opt/local/pgsql
|
||||
|
||||
4. Copy framework.conf.in to framework.conf, and edit the values accordingly.
|
||||
|
||||
5. If you want to codesign the appbundle, copy codesign.conf.in to
|
||||
3. If you want to codesign the appbundle, copy codesign.conf.in to
|
||||
codesign.conf and set the values accordingly.
|
||||
|
||||
6. To build, go to pgAdmin4 source root directory and execute "make appbundle".
|
||||
4. To build, go to pgAdmin4 source root directory and execute "make appbundle".
|
||||
This will create the python virtual environment and install all the required
|
||||
python modules mentioned in the requirements file using pip, build the
|
||||
runtime code and finally create the app bundle and the DMG in ./dist
|
||||
|
@ -13,58 +13,160 @@ _setup_env() {
|
||||
|
||||
_cleanup() {
|
||||
echo Cleaning up the old environment and app bundle...
|
||||
rm -rf ${SOURCE_DIR}/runtime/*.app
|
||||
rm -rf ${BUILD_ROOT}
|
||||
rm -rf "${BUILD_ROOT}"
|
||||
rm -rf "${TEMP_DIR}"
|
||||
rm -f ${DIST_ROOT}/*.dmg
|
||||
}
|
||||
|
||||
_create_venv() {
|
||||
_build_runtime() {
|
||||
echo "Assembling the runtime environment..."
|
||||
test -d "${BUILD_ROOT}" || mkdir "${BUILD_ROOT}"
|
||||
|
||||
# Copy in the template application
|
||||
cd "${BUILD_ROOT}"
|
||||
yarn --cwd "${BUILD_ROOT}" add nw
|
||||
cp -R "${BUILD_ROOT}/node_modules/nw/nwjs/nwjs.app" "${BUILD_ROOT}/"
|
||||
mv "${BUILD_ROOT}/nwjs.app" "${BUNDLE_DIR}"
|
||||
|
||||
# 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/"
|
||||
|
||||
# Install the runtime node_modules, then replace the package.json
|
||||
yarn --cwd "${BUNDLE_DIR}/Contents/Resources/app.nw/" install --production=true
|
||||
}
|
||||
|
||||
_create_python_env() {
|
||||
echo "Creating the Python environment..."
|
||||
PATH=${PGADMIN_POSTGRES_DIR}/bin:${PATH}
|
||||
LD_LIBRARY_PATH=${PGADMIN_POSTGRES_DIR}/lib:${LD_LIBRARY_PATH}
|
||||
|
||||
test -d ${BUILD_ROOT} || mkdir ${BUILD_ROOT}
|
||||
cd ${BUILD_ROOT}
|
||||
git clone https://github.com/gregneagle/relocatable-python.git "${BUILD_ROOT}/relocatable_python"
|
||||
PATH=$PATH:/usr/local/pgsql/bin "${BUILD_ROOT}/relocatable_python/make_relocatable_python_framework.py" --upgrade-pip --python-version ${PGADMIN_PYTHON_VERSION} --pip-requirements "${SOURCE_DIR}/requirements.txt" --destination "${BUNDLE_DIR}/Contents/Frameworks/"
|
||||
|
||||
${PYTHON_EXE} -m venv --copies venv
|
||||
|
||||
source venv/bin/activate
|
||||
pip install --no-cache-dir --no-binary psycopg2 -r ${SOURCE_DIR}/requirements.txt
|
||||
|
||||
# Figure the source path for use when completing the venv
|
||||
SOURCE_PYMODULES_PATH=$(dirname $("${PYTHON_EXE}" -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"))
|
||||
|
||||
# Figure the target path for use when completing the venv
|
||||
# Use "python" here as we want the venv path
|
||||
TARGET_PYMODULES_PATH=$(dirname $(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"))
|
||||
|
||||
# Copy in the additional system python modules
|
||||
cp -R ${SOURCE_PYMODULES_PATH}/* "${TARGET_PYMODULES_PATH}/"
|
||||
|
||||
# Link the python<version> directory to python so that the private environment path is found by the application.
|
||||
ln -s "$(basename ${TARGET_PYMODULES_PATH})" "${TARGET_PYMODULES_PATH}/../python"
|
||||
|
||||
# Remove tests
|
||||
find venv -name "test" -type d -print0 | xargs -0 rm -rf
|
||||
find venv -name "tests" -type d -print0 | xargs -0 rm -rf
|
||||
}
|
||||
|
||||
_build_runtime() {
|
||||
cd ${SOURCE_DIR}/runtime
|
||||
if [ -f Makefile ]; then
|
||||
make clean
|
||||
fi
|
||||
${QMAKE}
|
||||
make
|
||||
cp -r pgAdmin4.app "${BUNDLE_DIR}"
|
||||
# Remove some things we don't need
|
||||
cd "${BUNDLE_DIR}/Contents/Frameworks/Python.framework"
|
||||
find . -name test -type d -print0 | xargs -0 rm -rf
|
||||
find . -name tkinter -type d -print0 | xargs -0 rm -rf
|
||||
find . -name turtle.py -type f -print0 | xargs -0 rm -rf
|
||||
find . -name turtledemo -type d -print0 | xargs -0 rm -rf
|
||||
find . -name tcl* -type d -print0 | xargs -0 rm -rf
|
||||
find . -name tk* -type d -print0 | xargs -0 rm -rf
|
||||
find . -name tdbc* -type d -print0 | xargs -0 rm -rf
|
||||
find . -name itcl* -type d -print0 | xargs -0 rm -rf
|
||||
rm -f Versions/Current/lib/Tk.*
|
||||
rm -f Versions/Current/lib/libtcl*.dylib
|
||||
rm -f Versions/Current/lib/libtk*.dylib
|
||||
rm -f Versions/Current/lib/tcl*.sh
|
||||
rm -f Versions/Current/lib/tk*.sh
|
||||
rm -rf Versions/Current/share
|
||||
}
|
||||
|
||||
_build_docs() {
|
||||
cd ${SOURCE_DIR}/docs/en_US
|
||||
echo "Building the docs..."
|
||||
# Create a temporary venv for the doc build, so we don't contaminate the one
|
||||
# that we're going to ship.
|
||||
"${BUNDLE_DIR}/Contents/Frameworks/Python.framework/Versions/Current/bin/python3" -m venv "${BUILD_ROOT}/venv"
|
||||
source "${BUILD_ROOT}/venv/bin/activate"
|
||||
pip3 install --upgrade pip
|
||||
pip3 install -r "${SOURCE_DIR}/requirements.txt"
|
||||
pip3 install sphinx
|
||||
|
||||
cd "${SOURCE_DIR}"
|
||||
make docs
|
||||
|
||||
cd "${SOURCE_DIR}/docs/en_US"
|
||||
test -d "${BUNDLE_DIR}/Contents/Resources/docs/en_US" || mkdir -p "${BUNDLE_DIR}/Contents/Resources/docs/en_US"
|
||||
cp -r _build/html "${BUNDLE_DIR}/Contents/Resources/docs/en_US/"
|
||||
|
||||
# Remove some things we don't need
|
||||
rm -rf "${BUNDLE_DIR}/Contents/Resources/docs/en_US/html/_sources"
|
||||
rm -f "${BUNDLE_DIR}/Contents/Resources/docs/en_US/html/_static"/*.png
|
||||
}
|
||||
|
||||
_fixup_imports() {
|
||||
local TODO TODO_OLD FW_RELPATH LIB LIB_BN
|
||||
|
||||
echo "Fixing imports on the core appbundle..."
|
||||
pushd "$1" > /dev/null
|
||||
|
||||
# Find all the files that may need tweaks
|
||||
TODO=$(file `find . -perm +0111 -type f` | \
|
||||
grep -v "Frameworks/Python.framework" | \
|
||||
grep -v "Frameworks/nwjs" | \
|
||||
grep -E "Mach-O 64-bit" | \
|
||||
awk -F ':| ' '{ORS=" "; print $1}' | \
|
||||
uniq)
|
||||
|
||||
# Add anything in the site-packages Python directory
|
||||
TODO+=$(file `find ./Contents/Frameworks/Python.framework/Versions/Current/lib/python*/site-packages -perm +0111 -type f` | \
|
||||
grep -E "Mach-O 64-bit" | \
|
||||
awk -F ':| ' '{ORS=" "; print $1}' | \
|
||||
uniq)
|
||||
|
||||
echo "Found executables: ${TODO}"
|
||||
while test "${TODO}" != ""; do
|
||||
TODO_OLD=${TODO} ;
|
||||
TODO="" ;
|
||||
for TODO_OBJ in ${TODO_OLD}; do
|
||||
echo "Post-processing: ${TODO_OBJ}"
|
||||
|
||||
# Figure out the relative path from ${TODO_OBJ} to Contents/Frameworks
|
||||
FW_RELPATH=$(echo "${TODO_OBJ}" | \
|
||||
sed -n 's|^\(\.//*\)\(\([^/][^/]*/\)*\)[^/][^/]*$|\2|gp' | \
|
||||
sed -n 's|[^/][^/]*/|../|gp' \
|
||||
)"Contents/Frameworks"
|
||||
|
||||
# Find all libraries ${TODO_OBJ} depends on, but skip system libraries
|
||||
for LIB in $(
|
||||
otool -L ${TODO_OBJ} | \
|
||||
sed -n 's|^.*[[:space:]]\([^[:space:]]*\.dylib\).*$|\1|p' | \
|
||||
egrep -v '^(/usr/lib)|(/System)|@executable_path' \
|
||||
); do
|
||||
# Copy in any required dependencies
|
||||
LIB_BN="$(basename "${LIB}")" ;
|
||||
if ! test -f "Contents/Frameworks/${LIB_BN}"; then
|
||||
TARGET_FILE=""
|
||||
TARGET_PATH=""
|
||||
echo "Adding symlink: ${LIB_BN} (because of: ${TODO_OBJ})"
|
||||
cp -R "${LIB}" "Contents/Frameworks/${LIB_BN}"
|
||||
if ! test -L "Contents/Frameworks/${LIB_BN}"; then
|
||||
chmod 755 "Contents/Frameworks/${LIB_BN}"
|
||||
else
|
||||
TARGET_FILE=$(readlink "${LIB}")
|
||||
TARGET_PATH=$(dirname "${LIB}")/${TARGET_FILE}
|
||||
echo "Adding symlink target: ${TARGET_PATH}"
|
||||
cp "${TARGET_PATH}" "Contents/Frameworks/${TARGET_FILE}"
|
||||
chmod 755 "Contents/Frameworks/${TARGET_FILE}"
|
||||
fi
|
||||
echo "Rewriting ID in Contents/Frameworks/${LIB_BN} to ${LIB_BN}"
|
||||
install_name_tool \
|
||||
-id "${LIB_BN}" \
|
||||
"Contents/Frameworks/${LIB_BN}" || exit 1
|
||||
TODO="${TODO} ./Contents/Frameworks/${LIB_BN}"
|
||||
fi
|
||||
# Rewrite the dependency paths
|
||||
echo "Rewriting library ${LIB} to @loader_path/${FW_RELPATH}/${LIB_BN} in ${TODO_OBJ}"
|
||||
install_name_tool -change \
|
||||
"${LIB}" \
|
||||
"@loader_path/${FW_RELPATH}/${LIB_BN}" \
|
||||
"${TODO_OBJ}" || exit 1
|
||||
install_name_tool -change \
|
||||
"${TARGET_PATH}" \
|
||||
"@loader_path/${FW_RELPATH}/${TARGET_FILE}" \
|
||||
"${TODO_OBJ}" || exit 1
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
echo "Imports updated on the core appbundle."
|
||||
popd > /dev/null
|
||||
}
|
||||
|
||||
_complete_bundle() {
|
||||
echo "Completing the appbundle..."
|
||||
cd ${SCRIPT_DIR}
|
||||
|
||||
# Copy the binary utilities into place
|
||||
@ -73,129 +175,35 @@ _complete_bundle() {
|
||||
cp "${PGADMIN_POSTGRES_DIR}/bin/pg_dumpall" "${BUNDLE_DIR}/Contents/SharedSupport/"
|
||||
cp "${PGADMIN_POSTGRES_DIR}/bin/pg_restore" "${BUNDLE_DIR}/Contents/SharedSupport/"
|
||||
cp "${PGADMIN_POSTGRES_DIR}/bin/psql" "${BUNDLE_DIR}/Contents/SharedSupport/"
|
||||
|
||||
# Replace the place holders with the current version
|
||||
sed -e "s/PGADMIN_LONG_VERSION/${APP_LONG_VERSION}/g" -e "s/PGADMIN_SHORT_VERSION/${APP_SHORT_VERSION}/g" pgadmin.Info.plist.in > pgadmin.Info.plist
|
||||
|
||||
# copy Python private environment to app bundle
|
||||
cp -PR ${BUILD_ROOT}/venv "${BUNDLE_DIR}/Contents/Resources/"
|
||||
# Update the plist
|
||||
cp Info.plist.in "${BUNDLE_DIR}/Contents/Info.plist"
|
||||
sed -i '' "s/%APPNAME%/${APP_NAME}/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
||||
sed -i '' "s/%APPVER%/${APP_LONG_VERSION}/g" "${BUNDLE_DIR}/Contents/Info.plist"
|
||||
sed -i '' "s/%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 - 2021, The pgAdmin Development Team";/g' "${FILE}"
|
||||
sed -i '' 's/NSHumanReadableCopyright =.*/NSHumanReadableCopyright = "Copyright (C) 2013 - 2021, The pgAdmin Development Team";/g' "${FILE}"
|
||||
echo CFBundleDisplayName = \"${APP_NAME}\"\; >> "${FILE}"
|
||||
done
|
||||
|
||||
# Remove any TCL-related files that may cause us problems
|
||||
find "${BUNDLE_DIR}/Contents/Resources/venv/" -name "_tkinter*" -print0 | xargs -0 rm -f
|
||||
# PkgInfo
|
||||
echo APPLPGA4 > "${BUNDLE_DIR}/Contents/PkgInfo"
|
||||
|
||||
test -d "${BUNDLE_DIR}/Contents/Resources" || mkdir -p "${BUNDLE_DIR}/Contents/Resources"
|
||||
# Create qt.conf so that app knows where the Plugins are present
|
||||
cat >> "${BUNDLE_DIR}/Contents/Resources/qt.conf" << EOF
|
||||
[Paths]
|
||||
Plugins = PlugIns
|
||||
EOF
|
||||
# Icon
|
||||
cp pgAdmin4.icns "${BUNDLE_DIR}/Contents/Resources/app.icns"
|
||||
|
||||
test -d "${BUNDLE_DIR}/Contents/Frameworks" || mkdir -p "${BUNDLE_DIR}/Contents/Frameworks"
|
||||
test -d "${BUNDLE_DIR}/Contents/PlugIns/platforms" || mkdir -p "${BUNDLE_DIR}/Contents/PlugIns/platforms"
|
||||
test -d "${BUNDLE_DIR}/Contents/PlugIns/imageformats" || mkdir -p "${BUNDLE_DIR}/Contents/PlugIns/imageformats"
|
||||
cp -f ${PGADMIN_QT_DIR}/plugins/platforms/libqcocoa.dylib "${BUNDLE_DIR}/Contents/PlugIns/platforms"
|
||||
cp -f ${PGADMIN_QT_DIR}/plugins/imageformats/libqsvg.dylib "${BUNDLE_DIR}/Contents/PlugIns/imageformats"
|
||||
cp -f ${PGADMIN_POSTGRES_DIR}/lib/libpq.5.dylib "${BUNDLE_DIR}/Contents/Frameworks"
|
||||
# Rename the executable
|
||||
mv "${BUNDLE_DIR}/Contents/MacOS/nwjs" "${BUNDLE_DIR}/Contents/MacOS/${APP_NAME}"
|
||||
|
||||
local todo todo_old fw_relpath lib lib_bn
|
||||
# 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"
|
||||
|
||||
pushd "${BUNDLE_DIR}" > /dev/null
|
||||
# Import the dependencies, and rewrite any library references
|
||||
_fixup_imports "${BUNDLE_DIR}"
|
||||
|
||||
# We skip nested apps here - those are treated specially
|
||||
todo=$(file `find ./ -perm +0111 ! -type d ! -path "*.app/*" ! -name "*.app"` | grep -E "Mach-O 64-bit" | awk -F ':| ' '{ORS=" "; print $1}')
|
||||
|
||||
echo "Found executables: ${todo}"
|
||||
while test "${todo}" != ""; do
|
||||
todo_old=${todo} ;
|
||||
todo="" ;
|
||||
for todo_obj in ${todo_old}; do
|
||||
echo "Post-processing: ${todo_obj}"
|
||||
|
||||
# Figure out the relative path from todo_obj to Contents/Frameworks
|
||||
fw_relpath=$(echo "${todo_obj}" | sed -n 's|^\(\.//*\)\(\([^/][^/]*/\)*\)[^/][^/]*$|\2|gp' | sed -n 's|[^/][^/]*/|../|gp')"Contents/Frameworks"
|
||||
fw_relpath_old=${fw_relpath}
|
||||
|
||||
# Find all libraries $todo_obj depends on, but skip system libraries
|
||||
for lib in $(otool -L ${todo_obj} | grep "Qt\|dylib\|Frameworks\|PlugIns" | grep -v ":" | sed 's/(.*//' | egrep -v '(/usr/lib)|(/System)|@executable_path@'); do
|
||||
if echo ${lib} | grep "PlugIns\|libqcocoa" > /dev/null; then
|
||||
lib_loc="Contents/PlugIns/platforms"
|
||||
elif echo ${lib} | grep "PlugIns\|libqsvg" > /dev/null; then
|
||||
lib_loc="Contents/PlugIns/imageformats"
|
||||
elif echo ${lib} | grep "Qt" > /dev/null; then
|
||||
qtfw_path="$(dirname ${lib} | sed 's|.*\(Qt.*framework\)|\1|')"
|
||||
lib_loc="Contents/Frameworks/${qtfw_path}"
|
||||
if [ "$(basename ${todo_obj})" = "${lib}" ]; then
|
||||
lib_loc="$(dirname ${todo_obj})"
|
||||
qtfw_path=$(echo ${lib_loc} | sed 's/Contents\/Frameworks\///')
|
||||
fi
|
||||
elif echo ${lib} | grep "Python" > /dev/null; then
|
||||
pyfw_path="$(dirname ${lib} | sed 's|.*\(Python.*framework\)|\1|')"
|
||||
lib_loc="Contents/Frameworks/${pyfw_path}"
|
||||
if [ "$(basename ${todo_obj})" = "${lib}" ]; then
|
||||
lib_loc="$(dirname ${todo_obj})"
|
||||
pyfw_path=$(echo ${lib_loc} | sed 's/Contents\/Frameworks\///')
|
||||
fi
|
||||
else
|
||||
lib_loc="Contents/Frameworks"
|
||||
fi
|
||||
lib_bn="$(basename "${lib}")" ;
|
||||
if ! test -f "${lib_loc}/${lib_bn}"; then
|
||||
target_file=""
|
||||
target_path=""
|
||||
echo "Adding symlink: ${lib_bn} (because of: ${todo_obj})"
|
||||
|
||||
# Copy the QT and Python framework
|
||||
if echo ${lib} | grep Qt > /dev/null ; then
|
||||
test -d ${lib_loc} || mkdir -p ${lib_loc}
|
||||
echo Copying -R ${PGADMIN_QT_DIR}/lib/${qtfw_path}/${lib_bn} to ${lib_loc}/
|
||||
cp ${PGADMIN_QT_DIR}/lib/${qtfw_path}/${lib_bn} ${lib_loc}/
|
||||
elif echo ${lib} | grep Python > /dev/null ; then
|
||||
test -d ${lib_loc} || mkdir -p ${lib_loc}
|
||||
cp -R "${lib}" "${lib_loc}/${lib_bn}"
|
||||
else
|
||||
cp -R "${lib}" "${lib_loc}/${lib_bn}"
|
||||
fi
|
||||
if ! test -L "${lib_loc}/${lib_bn}"; then
|
||||
chmod 755 "${lib_loc}/${lib_bn}"
|
||||
else
|
||||
target_file=$(readlink "${lib}")
|
||||
target_path=$(dirname "${lib}")/${target_file}
|
||||
|
||||
echo "Adding symlink target: ${target_path}"
|
||||
cp "${target_path}" "${lib_loc}/${target_file}"
|
||||
chmod 755 "${lib_loc}/${target_file}"
|
||||
fi
|
||||
echo "Rewriting ID in ${lib_loc}/${lib_bn} to ${lib_bn}"
|
||||
install_name_tool -id "${lib_bn}" "${lib_loc}/${lib_bn}"
|
||||
|
||||
todo="${todo} ./${lib_loc}/${lib_bn}"
|
||||
fi
|
||||
if echo ${lib} | grep Qt > /dev/null ; then
|
||||
fw_relpath="${fw_relpath}/${qtfw_path}"
|
||||
fi
|
||||
if echo ${lib} | grep Python > /dev/null ; then
|
||||
fw_relpath="${fw_relpath}/${pyfw_path}"
|
||||
fi
|
||||
chmod +w ${todo_obj}
|
||||
echo "Rewriting library ${lib} to @loader_path/${fw_relpath}/${lib_bn} in ${todo_obj}"
|
||||
|
||||
install_name_tool -change "${lib}" "@loader_path/${fw_relpath}/${lib_bn}" "${todo_obj}"
|
||||
install_name_tool -change "${target_path}" "@loader_path/${fw_relpath}/${target_file}" "${todo_obj}"
|
||||
|
||||
fw_relpath="${fw_relpath_old}"
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
# Fix the rpaths for psycopg module
|
||||
find "${BUNDLE_DIR}/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libpq.5.dylib @loader_path/../../../../../../Frameworks/libpq.5.dylib
|
||||
find "${BUNDLE_DIR}/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libssl.1.0.0.dylib @loader_path/../../../../../../Frameworks/libssl.1.0.0.dylib
|
||||
find "${BUNDLE_DIR}/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libcrypto.1.0.0.dylib @loader_path/../../../../../../Frameworks/libcrypto.1.0.0.dylib
|
||||
|
||||
echo "App completed: ${BUNDLE_DIR}"
|
||||
popd > /dev/null
|
||||
|
||||
pushd ${SOURCE_DIR}/web > /dev/null
|
||||
# Build node modules
|
||||
pushd "${SOURCE_DIR}/web" > /dev/null
|
||||
yarn install
|
||||
yarn run bundle
|
||||
|
||||
@ -203,7 +211,7 @@ EOF
|
||||
popd > /dev/null
|
||||
|
||||
# copy the web directory to the bundle as it is required by runtime
|
||||
cp -r ${SOURCE_DIR}/web "${BUNDLE_DIR}/Contents/Resources/"
|
||||
cp -r "${SOURCE_DIR}/web" "${BUNDLE_DIR}/Contents/Resources/"
|
||||
cd "${BUNDLE_DIR}/Contents/Resources/web"
|
||||
rm -f pgadmin4.db config_local.*
|
||||
rm -rf karma.conf.js package.json node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
|
||||
@ -226,66 +234,6 @@ EOF
|
||||
find "${BUNDLE_DIR}" -name "*.pyc" -print0 | xargs -0 rm -f
|
||||
}
|
||||
|
||||
_framework_config() {
|
||||
# Get the config
|
||||
source ${SCRIPT_DIR}/framework.conf
|
||||
|
||||
echo Reorganising the framework structure...
|
||||
|
||||
# Create "Current" and "Current/Resources" inside each of the framework dirs
|
||||
find "${BUNDLE_DIR}/Contents/Frameworks"/*framework -type d -name "Versions" | while read -r framework_dir; do
|
||||
pushd "${framework_dir}" > /dev/null
|
||||
|
||||
# Create framework 'Current' soft link
|
||||
VERSION_NUMBER=`ls -1`
|
||||
ln -s ${VERSION_NUMBER} Current
|
||||
|
||||
# Create "Resources" subdirectory
|
||||
if [ ! -d Current/Resources ]; then
|
||||
mkdir Current/Resources
|
||||
fi
|
||||
|
||||
popd > /dev/null
|
||||
done
|
||||
|
||||
# Stuff for Qt framework files only
|
||||
find "${BUNDLE_DIR}/Contents/Frameworks" -type d -name "Qt*framework" | while read -r framework_dir; do
|
||||
pushd "${framework_dir}" > /dev/null
|
||||
|
||||
# Create soft link to the framework binary
|
||||
ln -s Versions/Current/Qt*
|
||||
|
||||
# Create soft link to the framework Resources dir
|
||||
ln -s Versions/Current/Resources
|
||||
|
||||
# Create the Info.plist files
|
||||
MYNAME=`ls -1 Qt*`
|
||||
if [ -f Resources/Info.plist ]; then
|
||||
chmod +w Resources/Info.plist
|
||||
fi
|
||||
sed 's/__SHORT_VERSION__/${QT_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Qt5" | sed 's/__FULL_VERSION__/${QT_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist"
|
||||
|
||||
popd > /dev/null
|
||||
done
|
||||
|
||||
# Same thing, but specific to the Python framework dir
|
||||
find "${BUNDLE_DIR}/Contents/Frameworks" -type d -name "P*framework" | while read -r framework_dir; do
|
||||
pushd "${framework_dir}" > /dev/null
|
||||
|
||||
# Create soft link to the framework binary
|
||||
ln -s Versions/Current/Py*
|
||||
|
||||
# Create soft link to the framework Resources dir
|
||||
ln -s Versions/Current/Resources
|
||||
|
||||
# Create the Info.plist file
|
||||
MYNAME=`ls -1 Py*`
|
||||
sed 's/__SHORT_VERSION__/${PYTHON_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Python" | sed 's/__FULL_VERSION__/${PYTHON_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist"
|
||||
|
||||
popd > /dev/null
|
||||
done
|
||||
}
|
||||
|
||||
_codesign_binaries() {
|
||||
if [ ${CODESIGN} -eq 0 ]; then
|
||||
return
|
||||
@ -300,17 +248,22 @@ _codesign_binaries() {
|
||||
echo "Developer Bundle Identifier not found in codesign.conf" >&2
|
||||
fi
|
||||
|
||||
# Create the entitlements file
|
||||
cp "${SCRIPT_DIR}/entitlements.plist.in" "${BUILD_ROOT}/entitlements.plist"
|
||||
TEAM_ID=$(echo ${DEVELOPER_ID} | awk -F"[()]" '{print $2}')
|
||||
sed -i '' "s/%TEAMID%/${TEAM_ID}/g" "${BUILD_ROOT}/entitlements.plist"
|
||||
|
||||
echo Signing ${BUNDLE_DIR} binaries...
|
||||
IFS=$'\n'
|
||||
for i in $(find "${BUNDLE_DIR}" -type f -perm +111 -exec file "{}" \; | grep -E "Mach-O executable|Mach-O 64-bit executable|Mach-O 64-bit bundle" | awk -F":| \\\(" '{print $1}' | uniq)
|
||||
for i in $(find "${BUNDLE_DIR}" -type f -perm +111 -exec file "{}" \; | grep -v "(for architecture" | grep -E "Mach-O executable|Mach-O 64-bit executable|Mach-O 64-bit bundle" | awk -F":" '{print $1}' | uniq)
|
||||
do
|
||||
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
|
||||
codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements "${BUILD_ROOT}/entitlements.plist" -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
|
||||
done
|
||||
|
||||
echo Signing ${BUNDLE_DIR} libraries...
|
||||
for i in $(find "${BUNDLE_DIR}" -type f -name "*.dylib*")
|
||||
do
|
||||
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
|
||||
codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements "${BUILD_ROOT}/entitlements.plist" -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
|
||||
done
|
||||
}
|
||||
|
||||
@ -321,44 +274,32 @@ _codesign_bundle() {
|
||||
|
||||
# Sign the .app
|
||||
echo Signing ${BUNDLE_DIR}...
|
||||
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${BUNDLE_DIR}"
|
||||
|
||||
# Verify it worked
|
||||
echo Verifying the signature...
|
||||
codesign --verify --verbose --deep --force "${BUNDLE_DIR}"
|
||||
echo ${BUNDLE_DIR} successfully signed.
|
||||
codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements "${BUILD_ROOT}/entitlements.plist" -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${BUNDLE_DIR}"
|
||||
}
|
||||
|
||||
_create_dmg() {
|
||||
# move to the directory where we want to create the DMG
|
||||
test -d ${DIST_ROOT} || mkdir ${DIST_ROOT}
|
||||
cd ${DIST_ROOT}
|
||||
|
||||
DMG_LICENCE=./../pkg/mac/licence.rtf
|
||||
DMG_VOLUME_NAME=${APP_NAME}
|
||||
DMG_NAME=`echo ${DMG_VOLUME_NAME} | sed 's/ //g' | awk '{print tolower($0)}'`
|
||||
DMG_IMAGE=${DMG_NAME}-${APP_LONG_VERSION}.dmg
|
||||
echo "Checking out create-dmg..."
|
||||
git clone https://github.com/create-dmg/create-dmg.git "${BUILD_ROOT}/create-dmg"
|
||||
|
||||
DMG_DIR=./${DMG_IMAGE}.src
|
||||
|
||||
if test -e "${DMG_DIR}"; then
|
||||
echo "Directory ${DMG_DIR} already exists. Please delete it manually." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Cleaning up"
|
||||
rm -f "${DMG_IMAGE}"
|
||||
mkdir "${DMG_DIR}"
|
||||
|
||||
echo "Copying data into temporary directory"
|
||||
cp -R "${BUNDLE_DIR}" "${DMG_DIR}"
|
||||
|
||||
echo "Creating image"
|
||||
hdiutil create -quiet -srcfolder "$DMG_DIR" -fs HFS+ -format UDZO -volname "${DMG_VOLUME_NAME}" -ov "${DMG_IMAGE}"
|
||||
rm -rf "${DMG_DIR}"
|
||||
|
||||
echo Attaching License to image...
|
||||
python ${SCRIPT_DIR}/dmg-license.py "${DMG_IMAGE}" "${DMG_LICENCE}" -c bz2
|
||||
"${BUILD_ROOT}/create-dmg/create-dmg" \
|
||||
--volname "${APP_NAME}" \
|
||||
--volicon "${SCRIPT_DIR}/dmg-icon.icns" \
|
||||
--eula "${SCRIPT_DIR}/licence.rtf" \
|
||||
--background "${SCRIPT_DIR}/dmg-background.png" \
|
||||
--app-drop-link 600 220 \
|
||||
--icon "${APP_NAME}.app" 200 220 \
|
||||
--window-pos 200 120 \
|
||||
--window-size 800 400 \
|
||||
--hide-extension "${APP_NAME}.app" \
|
||||
--add-file .DS_Store "${SCRIPT_DIR}/dmg.DS_Store" 5 5 \
|
||||
--format UDBZ \
|
||||
--skip-jenkins \
|
||||
--no-internet-enable \
|
||||
"${DIST_ROOT}/$(echo ${APP_NAME} | sed 's/ //g' | awk '{print tolower($0)}')-${APP_LONG_VERSION}.dmg" \
|
||||
"${BUNDLE_DIR}"
|
||||
}
|
||||
|
||||
_codesign_dmg() {
|
||||
@ -366,21 +307,7 @@ _codesign_dmg() {
|
||||
return
|
||||
fi
|
||||
|
||||
DMG_VOLUME_NAME=${APP_NAME}
|
||||
DMG_NAME=`echo ${DMG_VOLUME_NAME} | sed 's/ //g' | awk '{print tolower($0)}'`
|
||||
DMG_IMAGE=${DIST_ROOT}/${DMG_NAME}-${APP_LONG_VERSION}.dmg
|
||||
|
||||
if ! test -f "${DMG_IMAGE}" ; then
|
||||
echo "${DMG_IMAGE} is no disk image!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sign the .app
|
||||
echo Signing ${DMG_IMAGE}...
|
||||
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${DMG_IMAGE}"
|
||||
|
||||
# Verify it worked
|
||||
echo Verifying the signature...
|
||||
codesign --verify --verbose --force "${DMG_IMAGE}"
|
||||
echo ${DMG_IMAGE} successfully signed.
|
||||
}
|
||||
echo Signing disk image...
|
||||
codesign --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${DIST_ROOT}/$(echo ${APP_NAME} | sed 's/ //g' | awk '{print tolower($0)}')-${APP_LONG_VERSION}.dmg"
|
||||
}
|
||||
|
@ -12,21 +12,14 @@ trap 'if [ $? -ne 0 ]; then echo "\"${last_command}\" command filed with exit co
|
||||
SCRIPT_DIR=$(cd `dirname $0` && pwd)
|
||||
SOURCE_DIR=$(realpath ${SCRIPT_DIR}/../..)
|
||||
BUILD_ROOT=$(realpath ${SCRIPT_DIR}/../..)/mac-build
|
||||
TEMP_DIR=$(realpath ${SCRIPT_DIR}/../..)/mac-temp
|
||||
DIST_ROOT=$(realpath ${SCRIPT_DIR}/../..)/dist
|
||||
|
||||
if [ ! -f ${SCRIPT_DIR}/framework.conf ]; then
|
||||
echo
|
||||
echo "Error: pkg/mac/framework.conf not found!"
|
||||
echo "Copy pkg/mac/framework.conf.in to pkg/mac/framework.conf and edit as required for the current system."
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CODESIGN=1
|
||||
if [ ! -f ${SCRIPT_DIR}/codesign.conf ]; then
|
||||
echo
|
||||
echo "******************************************************************"
|
||||
echo "* ${SCRIPT_DIR}/codesign.conf not found. NOT signing the binaries."
|
||||
echo "* pkg/mac/codesign.conf not found. NOT signing the binaries."
|
||||
echo "******************************************************************"
|
||||
echo
|
||||
CODESIGN=0
|
||||
@ -35,50 +28,24 @@ else
|
||||
source ${SCRIPT_DIR}/codesign.conf
|
||||
fi
|
||||
|
||||
if [ "x${PGADMIN_PYTHON_DIR}" == "x" ]; then
|
||||
echo "PGADMIN_PYTHON_DIR not set. Setting it to the default: /usr/local/python"
|
||||
export PGADMIN_PYTHON_DIR=/usr/local/python
|
||||
fi
|
||||
PYTHON_EXE=${PGADMIN_PYTHON_DIR}/bin/python3
|
||||
|
||||
# Check if Python is working and calculate PYTHON_VERSION
|
||||
if ${PYTHON_EXE} -V > /dev/null 2>&1; then
|
||||
PYTHON_VERSION=`${PYTHON_EXE} -V 2>&1 | awk '{print $2}' | cut -d"." -f1-2 | sed 's/\.//'`
|
||||
else
|
||||
echo "Error: Python installation missing!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${PYTHON_VERSION}" -gt "38" ] && [ "${PYTHON_VERSION}" -lt "34" ]; then
|
||||
echo "Python version not supported."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "x${PGADMIN_QT_DIR}" == "x" ]; then
|
||||
echo "PGADMIN_QT_DIR not set. Setting it to the default: ~/Qt/5.13.2/clang_64"
|
||||
export PGADMIN_QT_DIR=~/Qt/5.13.2/clang_64
|
||||
fi
|
||||
|
||||
QMAKE=${PGADMIN_QT_DIR}/bin/qmake
|
||||
if ! ${QMAKE} --version > /dev/null 2>&1; then
|
||||
echo "Error: qmake not found. QT installation is not present or incomplete."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "x${PGADMIN_POSTGRES_DIR}" == "x" ]; then
|
||||
echo "PGADMIN_POSTGRES_DIR not set. Setting it to the default: /usr/local/pgsql"
|
||||
export PGADMIN_POSTGRES_DIR=/usr/local/pgsql
|
||||
fi
|
||||
|
||||
if [ "x${PGADMIN_PYTHON_VERSION}" == "x" ]; then
|
||||
echo "PGADMIN_PYTHON_VERSION not set. Setting it to the default: 3.9.0"
|
||||
export PGADMIN_PYTHON_VERSION=3.9.0
|
||||
fi
|
||||
|
||||
source ${SCRIPT_DIR}/build-functions.sh
|
||||
|
||||
_setup_env
|
||||
_cleanup
|
||||
_create_venv
|
||||
_build_runtime
|
||||
_create_python_env
|
||||
_build_docs
|
||||
_complete_bundle
|
||||
_framework_config
|
||||
_codesign_binaries
|
||||
_codesign_bundle
|
||||
_create_dmg
|
||||
|
BIN
pkg/mac/dmg-background.png
Normal file
After Width: | Height: | Size: 701 KiB |
BIN
pkg/mac/dmg-icon.icns
Normal file
@ -1,166 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
"""
|
||||
This script adds a license file to a DMG. Requires Xcode and a plain ascii text
|
||||
license file.
|
||||
Obviously only runs on a Mac.
|
||||
|
||||
Copyright (C) 2011-2013 Jared Hobbs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import optparse
|
||||
|
||||
|
||||
class Path(str):
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
os.unlink(self)
|
||||
|
||||
|
||||
def mktemp(dir=None, suffix=''):
|
||||
(fd, filename) = tempfile.mkstemp(dir=dir, suffix=suffix)
|
||||
os.close(fd)
|
||||
return Path(filename)
|
||||
|
||||
|
||||
def main(options, args):
|
||||
dmg_file, license_file = args
|
||||
with mktemp('.') as tmp_file:
|
||||
with open(tmp_file, 'w') as f:
|
||||
f.write("""data 'TMPL' (128, "LPic") {
|
||||
$"1344 6566 6175 6C74 204C 616E 6775 6167"
|
||||
$"6520 4944 4457 5244 0543 6F75 6E74 4F43"
|
||||
$"4E54 042A 2A2A 2A4C 5354 430B 7379 7320"
|
||||
$"6C61 6E67 2049 4444 5752 441E 6C6F 6361"
|
||||
$"6C20 7265 7320 4944 2028 6F66 6673 6574"
|
||||
$"2066 726F 6D20 3530 3030 4457 5244 1032"
|
||||
$"2D62 7974 6520 6C61 6E67 7561 6765 3F44"
|
||||
$"5752 4404 2A2A 2A2A 4C53 5445"
|
||||
};
|
||||
|
||||
data 'LPic' (5000) {
|
||||
$"0000 0002 0000 0000 0000 0000 0004 0000"
|
||||
};
|
||||
|
||||
data 'STR#' (5000, "English buttons") {
|
||||
$"0006 0D45 6E67 6C69 7368 2074 6573 7431"
|
||||
$"0541 6772 6565 0844 6973 6167 7265 6505"
|
||||
$"5072 696E 7407 5361 7665 2E2E 2E7A 4966"
|
||||
$"2079 6F75 2061 6772 6565 2077 6974 6820"
|
||||
$"7468 6520 7465 726D 7320 6F66 2074 6869"
|
||||
$"7320 6C69 6365 6E73 652C 2063 6C69 636B"
|
||||
$"2022 4167 7265 6522 2074 6F20 6163 6365"
|
||||
$"7373 2074 6865 2073 6F66 7477 6172 652E"
|
||||
$"2020 4966 2079 6F75 2064 6F20 6E6F 7420"
|
||||
$"6167 7265 652C 2070 7265 7373 2022 4469"
|
||||
$"7361 6772 6565 2E22"
|
||||
};
|
||||
|
||||
data 'STR#' (5002, "English") {
|
||||
$"0006 0745 6E67 6C69 7368 0541 6772 6565"
|
||||
$"0844 6973 6167 7265 6505 5072 696E 7407"
|
||||
$"5361 7665 2E2E 2E7B 4966 2079 6F75 2061"
|
||||
$"6772 6565 2077 6974 6820 7468 6520 7465"
|
||||
$"726D 7320 6F66 2074 6869 7320 6C69 6365"
|
||||
$"6E73 652C 2070 7265 7373 2022 4167 7265"
|
||||
$"6522 2074 6F20 696E 7374 616C 6C20 7468"
|
||||
$"6520 736F 6674 7761 7265 2E20 2049 6620"
|
||||
$"796F 7520 646F 206E 6F74 2061 6772 6565"
|
||||
$"2C20 7072 6573 7320 2244 6973 6167 7265"
|
||||
$"6522 2E"
|
||||
};\n\n""")
|
||||
with open(license_file, 'r') as lines:
|
||||
kind = 'RTF ' if license_file.lower().endswith('.rtf') \
|
||||
else 'TEXT'
|
||||
f.write('data \'%s\' (5000, "English") {\n' % kind)
|
||||
|
||||
def escape(s):
|
||||
return s.strip().replace('\\', '\\\\').replace('"', '\\"')
|
||||
|
||||
for line in lines:
|
||||
if len(line) < 1000:
|
||||
f.write(' "' + escape(line) + '\\n"\n')
|
||||
else:
|
||||
for liner in line.split('.'):
|
||||
f.write(' "' + escape(liner) + '. \\n"\n')
|
||||
f.write('};\n\n')
|
||||
f.write("""data 'styl' (5000, "English") {
|
||||
$"0003 0000 0000 000C 0009 0014 0000 0000"
|
||||
$"0000 0000 0000 0000 0027 000C 0009 0014"
|
||||
$"0100 0000 0000 0000 0000 0000 002A 000C"
|
||||
$"0009 0014 0000 0000 0000 0000 0000"
|
||||
};\n""")
|
||||
os.system('hdiutil unflatten -quiet "%s"' % dmg_file)
|
||||
ret = os.system('%s -a %s -o "%s"' %
|
||||
(options.rez, tmp_file, dmg_file))
|
||||
os.system('hdiutil flatten -quiet "%s"' % dmg_file)
|
||||
if options.compression is not None:
|
||||
os.system('cp %s %s.temp.dmg' % (dmg_file, dmg_file))
|
||||
os.remove(dmg_file)
|
||||
if options.compression == "bz2":
|
||||
os.system('hdiutil convert %s.temp.dmg -format UDBZ -o %s' %
|
||||
(dmg_file, dmg_file))
|
||||
elif options.compression == "gz":
|
||||
os.system('hdiutil convert %s.temp.dmg -format ' % dmg_file +
|
||||
'UDZO -imagekey zlib-devel=9 -o %s' % dmg_file)
|
||||
os.remove('%s.temp.dmg' % dmg_file)
|
||||
if ret == 0:
|
||||
print("Successfully added license to '%s'" % dmg_file)
|
||||
else:
|
||||
print("Failed to add license to '%s'" % dmg_file)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = optparse.OptionParser()
|
||||
parser.set_usage("""%prog <dmgFile> <licenseFile> [OPTIONS]
|
||||
This program adds a software license agreement to a DMG file.
|
||||
It requires Xcode and either a plain ascii text <licenseFile>
|
||||
or a <licenseFile.rtf> with the RTF contents.
|
||||
|
||||
See --help for more details.""")
|
||||
parser.add_option(
|
||||
'--rez',
|
||||
'-r',
|
||||
action='store',
|
||||
default='/Applications/Xcode.app/Contents/Developer/Tools/Rez',
|
||||
help='The path to the Rez tool. Defaults to %default'
|
||||
)
|
||||
parser.add_option(
|
||||
'--compression',
|
||||
'-c',
|
||||
action='store',
|
||||
choices=['bz2', 'gz'],
|
||||
default=None,
|
||||
help='Optionally compress dmg using specified compression type. '
|
||||
'Choices are bz2 and gz.'
|
||||
)
|
||||
options, args = parser.parse_args()
|
||||
cond = len(args) != 2
|
||||
if not os.path.exists(options.rez):
|
||||
print('Failed to find Rez at "%s"!\n' % options.rez)
|
||||
cond = True
|
||||
if cond:
|
||||
parser.print_usage()
|
||||
sys.exit(1)
|
||||
main(options, args)
|
BIN
pkg/mac/dmg.DS_Store
Normal file
12
pkg/mac/entitlements.plist.in
Normal file
@ -0,0 +1,12 @@
|
||||
<?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>com.apple.security.app-sandbox</key>
|
||||
<false/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<string>%TEAMID%.org.pgadmin.pgadmin4</string>
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -1,10 +0,0 @@
|
||||
# Copy this file to framework.conf, and edit the values below to reflect your
|
||||
# environment (versions of Python/Qt used)
|
||||
|
||||
PYTHON_SHORT_VERSION=3.8
|
||||
PYTHON_FULL_VERSION=3.8.2
|
||||
|
||||
QT_SHORT_VERSION=5.14
|
||||
QT_FULL_VERSION=5.14.2
|
||||
|
||||
|
BIN
pkg/mac/pgAdmin4.icns
Normal file
@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>pgAdmin4</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>pgAdmin4 PGADMIN_LONG_VERSION</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>pgAdmin4.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.postgresql.pgadmin</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>PGADMIN_SHORT_VERSION</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>PGADMIN_LONG_VERSION</string>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -26,6 +26,12 @@ _build_runtime
|
||||
_build_docs "redhat"
|
||||
_copy_code
|
||||
|
||||
# Get an RPM-compatible version number
|
||||
RPM_VERSION=${APP_RELEASE}.${APP_REVISION}
|
||||
if [ ! -z ${APP_SUFFIX} ]; then
|
||||
RPM_VERSION=${RPM_VERSION}_${APP_SUFFIX}
|
||||
fi
|
||||
|
||||
#
|
||||
# Server package
|
||||
#
|
||||
@ -46,7 +52,7 @@ cat << EOF > "${BUILDROOT}/server.spec"
|
||||
%undefine __brp_ldconfig
|
||||
|
||||
Name: ${APP_NAME}-server
|
||||
Version: ${APP_LONG_VERSION}
|
||||
Version: ${RPM_VERSION}
|
||||
Release: 1%{?dist}
|
||||
Summary: The core server package for pgAdmin.
|
||||
License: PostgreSQL
|
||||
@ -84,12 +90,12 @@ cat << EOF > "${BUILDROOT}/desktop.spec"
|
||||
%undefine __brp_ldconfig
|
||||
|
||||
Name: ${APP_NAME}-desktop
|
||||
Version: ${APP_LONG_VERSION}
|
||||
Version: ${RPM_VERSION}
|
||||
Release: 1%{?dist}
|
||||
Summary: The desktop user interface for pgAdmin.
|
||||
License: PostgreSQL
|
||||
URL: https://www.pgadmin.org/
|
||||
Requires: ${APP_NAME}-server, qt5-qtbase, qt5-qtbase-gui
|
||||
Requires: ${APP_NAME}-server, libatomic
|
||||
|
||||
%description
|
||||
The desktop user interface for pgAdmin. 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.
|
||||
@ -99,9 +105,16 @@ The desktop user interface for pgAdmin. pgAdmin is the most popular and feature
|
||||
%install
|
||||
cp -rfa %{pga_build_root}/desktop/* \${RPM_BUILD_ROOT}
|
||||
|
||||
%post
|
||||
/bin/xdg-icon-resource forceupdate
|
||||
|
||||
%files
|
||||
/usr/pgadmin4/bin/*
|
||||
/usr/pgadmin4/share/*
|
||||
/usr/share/icons/hicolor/128x128/apps/*
|
||||
/usr/share/icons/hicolor/64x64/apps/*
|
||||
/usr/share/icons/hicolor/48x48/apps/*
|
||||
/usr/share/icons/hicolor/32x32/apps/*
|
||||
/usr/share/icons/hicolor/16x16/apps/*
|
||||
/usr/share/applications/*
|
||||
EOF
|
||||
|
||||
@ -124,7 +137,7 @@ cat << EOF > "${BUILDROOT}/web.spec"
|
||||
%undefine __brp_ldconfig
|
||||
|
||||
Name: ${APP_NAME}-web
|
||||
Version: ${APP_LONG_VERSION}
|
||||
Version: ${RPM_VERSION}
|
||||
Release: 1%{?dist}
|
||||
BuildArch: noarch
|
||||
Summary: The web interface for pgAdmin, hosted under Apache HTTPD.
|
||||
@ -171,7 +184,7 @@ cat << EOF > "${BUILDROOT}/meta.spec"
|
||||
%undefine __brp_ldconfig
|
||||
|
||||
Name: ${APP_NAME}
|
||||
Version: ${APP_LONG_VERSION}
|
||||
Version: ${RPM_VERSION}
|
||||
Release: 1%{?dist}
|
||||
BuildArch: noarch
|
||||
Summary: Installs all required components to run pgAdmin in desktop and web modes.
|
||||
@ -209,8 +222,8 @@ yumdownloader --downloadonly --destdir=$DISTROOT postgresql13-libs
|
||||
#
|
||||
# Get the results!
|
||||
#
|
||||
cp ${HOME}/rpmbuild/RPMS/${OS_ARCH}/${APP_NAME}-*${APP_LONG_VERSION}-*.${OS_ARCH}.rpm "${DISTROOT}/"
|
||||
cp ${HOME}/rpmbuild/RPMS/noarch/${APP_NAME}-*${APP_LONG_VERSION}-*.noarch.rpm "${DISTROOT}/"
|
||||
cp ${HOME}/rpmbuild/RPMS/${OS_ARCH}/${APP_NAME}-*${RPM_VERSION}-*.${OS_ARCH}.rpm "${DISTROOT}/"
|
||||
cp ${HOME}/rpmbuild/RPMS/noarch/${APP_NAME}-*${RPM_VERSION}-*.noarch.rpm "${DISTROOT}/"
|
||||
if [ ${OS_VERSION} == 7 ]; then
|
||||
cp ${HOME}/rpmbuild/RPMS/${OS_ARCH}/pgadmin4-python3-mod_wsgi-4.7.1-2.el7.x86_64.rpm "${DISTROOT}/"
|
||||
fi
|
||||
|
@ -35,10 +35,10 @@ echo "Installing build pre-requisites..."
|
||||
yum groupinstall -y "Development Tools"
|
||||
|
||||
if [ ${OS_VERSION} == 7 ]; then
|
||||
yum install -y expect fakeroot httpd-devel qt5-qtbase-devel postgresql12-devel python3-devel nodejs yarn rpm-build rpm-sign yum-utils krb5-devel
|
||||
yum install -y expect fakeroot httpd-devel postgresql12-devel python3-devel nodejs yarn rpm-build rpm-sign yum-utils krb5-devel
|
||||
pip3 install sphinx
|
||||
else
|
||||
yum install -y expect fakeroot qt5-qtbase-devel postgresql12-devel python3-devel python3-sphinx nodejs yarn rpm-build rpm-sign yum-utils krb5-devel
|
||||
yum install -y expect fakeroot postgresql12-devel python3-devel python3-sphinx nodejs yarn rpm-build rpm-sign yum-utils krb5-devel
|
||||
fi
|
||||
|
||||
# Setup RPM macros for signing
|
||||
|
@ -5,38 +5,29 @@ a 32bit build.
|
||||
Installing build requirements
|
||||
=============================
|
||||
|
||||
1) Install Qt 5.14.2: https://www.qt.io/download-qt-installer
|
||||
|
||||
Use the MSVC++ 2017 64bit option.
|
||||
|
||||
2) Install Visual Studio 2017 Pro: https://my.visualstudio.com/Downloads?q=Visual%20Studio%202017
|
||||
1) Install Visual Studio 2017 Pro: https://my.visualstudio.com/Downloads?q=Visual%20Studio%202017
|
||||
|
||||
Choose the Desktop development with C++ option.
|
||||
|
||||
3) Install Chocolatey: https://chocolatey.org/install#individual
|
||||
2) Install Chocolatey: https://chocolatey.org/install#individual
|
||||
|
||||
4) Install various command line tools:
|
||||
3) Install various command line tools:
|
||||
|
||||
choco install -y bzip2 cmake diffutils gawk gnuwin32-coreutils.install gzip git html-help-workshop innosetup nodejs-lts python sed strawberryperl wget yarn
|
||||
choco install -y bzip2 cmake diffutils dotnet3.5 gzip git innosetup nodejs-lts python strawberryperl wget yarn
|
||||
|
||||
5) Upgrade pip (this may give a permissions error that can be ignored):
|
||||
4) Upgrade pip (this may give a permissions error that can be ignored):
|
||||
|
||||
pip install --upgrade pip
|
||||
|
||||
6) Install virtualenv
|
||||
5) Install virtualenv
|
||||
|
||||
pip install virtualenv
|
||||
|
||||
7) Add the following paths to the system PATH:
|
||||
|
||||
C:\Program Files (x86)\GnuWin32\bin
|
||||
C:\Program Files (x86)\HTML Help Workshop
|
||||
|
||||
Building dependencies
|
||||
=====================
|
||||
|
||||
The following steps should be run from a Visual Studio 2017 64bit command
|
||||
prompt (except where noted).
|
||||
prompt.
|
||||
|
||||
1) Create a directory for the dependencies:
|
||||
|
||||
@ -165,9 +156,9 @@ Studio 2017 64bit command prompt. Note that the examples shown below are the
|
||||
defaults for the build system, so if they match your requirements you don't
|
||||
need to set them:
|
||||
|
||||
SET "PGADMIN_POSTGRES_DIR=C:\Program Files\PostgreSQL\12"
|
||||
SET "PGADMIN_PYTHON_DIR=C:\Python38"
|
||||
SET "PGADMIN_QT_DIR=C:\Qt\5.14.2\msvc2017_64"
|
||||
SET "PGADMIN_POSTGRES_DIR=C:\Program Files\PostgreSQL\13"
|
||||
SET "PGADMIN_PYTHON_DIR=C:\Python39"
|
||||
SET "PGADMIN_KRB5_DIR=C:\jenkins\build64\krb5"
|
||||
SET "PGADMIN_INNOTOOL_DIR=C:\Program Files (x86)\Inno Setup 6"
|
||||
SET "PGADMIN_SIGNTOOL_DIR=C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64"
|
||||
SET "PGADMIN_VCREDIST_DIR=C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Redist\MSVC\14.16.27012"
|
||||
@ -179,4 +170,4 @@ make
|
||||
If you have a code signing certificate, this will automatically be used if
|
||||
found in the Windows Certificate Store to sign the installer.
|
||||
|
||||
3) Find the completed installer in the dist/ subdirectory of your source tree.
|
||||
3) Find the completed installer in the dist/ subdirectory of your source tree.
|
||||
|
@ -33,6 +33,7 @@ ChangesEnvironment=yes
|
||||
;UninstallFilesDir={app}\{#MyAppVersion}
|
||||
ArchitecturesInstallIn64BitMode={#MyAppArchitecturesMode}
|
||||
AllowNoIcons=yes
|
||||
WizardImageFile=sidebar.bmp
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
@ -288,7 +289,7 @@ begin
|
||||
end;
|
||||
|
||||
// This function would be called during upgrade mode
|
||||
// In upgrade mode - delete venv/* for example
|
||||
// In upgrade mode - delete python/* for example
|
||||
procedure DelFolder(Path: string);
|
||||
var
|
||||
FindRec: TFindRec;
|
||||
@ -340,7 +341,7 @@ begin
|
||||
if (IsUpgradeMode) then
|
||||
begin
|
||||
DelWebfolder(ExpandConstant('{app}\web'));
|
||||
DelFolder(ExpandConstant('{app}\venv'));
|
||||
DelFolder(ExpandConstant('{app}\python'));
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
BIN
pkg/win32/sidebar.bmp
Normal file
After Width: | Height: | Size: 774 KiB |
@ -15,6 +15,7 @@
|
||||
# ignored when building a PIP Wheel.
|
||||
##############################################################################
|
||||
blinker==1.4
|
||||
cheroot==8.5.1
|
||||
Flask==1.0.2
|
||||
Werkzeug>=0.15.0
|
||||
Flask-Gravatar==0.5.0
|
||||
@ -38,8 +39,8 @@ psycopg2>=2.8
|
||||
python-dateutil>=2.8.0
|
||||
SQLAlchemy>=1.3.13
|
||||
Flask-Security-Too>=3.0.0,<4.0.0
|
||||
bcrypt<=3.1.7
|
||||
cryptography<=3.0
|
||||
bcrypt>=3.1.7
|
||||
cryptography>=3.0
|
||||
sshtunnel>=0.1.5
|
||||
ldap3>=2.5.1
|
||||
Flask-BabelEx>=0.9.4
|
||||
|
6
runtime/.eslintignore
Normal file
@ -0,0 +1,6 @@
|
||||
generated
|
||||
node_modules
|
||||
vendor
|
||||
templates/
|
||||
templates\
|
||||
ycache
|
53
runtime/.eslintrc.js
Normal file
@ -0,0 +1,53 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'amd': true,
|
||||
'jasmine': true,
|
||||
},
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
],
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 2018,
|
||||
"sourceType": "module",
|
||||
},
|
||||
'globals': {
|
||||
'_': true,
|
||||
'module': true,
|
||||
'process': true,
|
||||
'nw': true,
|
||||
'platform': true
|
||||
},
|
||||
'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,
|
||||
},
|
||||
};
|
13
runtime/.gitignore
vendored
@ -1,12 +1 @@
|
||||
.qmake.cache
|
||||
.qmake.stash
|
||||
Makefile
|
||||
moc_*.cpp
|
||||
moc_*.h
|
||||
pgAdmin4
|
||||
pgAdmin4.app/
|
||||
pgAdmin4.pro.user*
|
||||
qrc_breeze.cpp
|
||||
qrc_pgAdmin4.cpp
|
||||
ui_*.h
|
||||
object_script.*
|
||||
dev_config.json
|
@ -1,136 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ConfigWindow.h - Configuration window
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "ConfigWindow.h"
|
||||
#include "ui_ConfigWindow.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QTcpSocket>
|
||||
#include <QtWidgets>
|
||||
|
||||
ConfigWindow::ConfigWindow(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
initConfigWindow();
|
||||
}
|
||||
|
||||
void ConfigWindow::initConfigWindow()
|
||||
{
|
||||
ui = new Ui::ConfigWindow;
|
||||
ui->setupUi(this);
|
||||
|
||||
m_needRestart = false;
|
||||
|
||||
setConfigValues();
|
||||
}
|
||||
|
||||
void ConfigWindow::setConfigValues()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
ui->browserCommandLineEdit->setText(settings.value("BrowserCommand").toString());
|
||||
|
||||
if(settings.value("FixedPort").toBool())
|
||||
{
|
||||
ui->chkFixedPort->setCheckState(Qt::Checked);
|
||||
ui->spinPortNumber->setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->chkFixedPort->setCheckState(Qt::Unchecked);
|
||||
ui->spinPortNumber->setEnabled(false);
|
||||
}
|
||||
|
||||
ui->spinPortNumber->setValue(settings.value("PortNumber").toInt());
|
||||
|
||||
if (settings.value("OpenTabAtStartup", true).toBool())
|
||||
{
|
||||
ui->chkOpenTabAtStartup->setCheckState(Qt::Checked);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->chkOpenTabAtStartup->setCheckState(Qt::Unchecked);
|
||||
}
|
||||
|
||||
ui->pythonPathLineEdit->setText(settings.value("PythonPath").toString());
|
||||
ui->applicationPathLineEdit->setText(settings.value("ApplicationPath").toString());
|
||||
}
|
||||
|
||||
void ConfigWindow::on_buttonBox_accepted()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
// Save the settings, and return true if a restart is required, otherwise false.
|
||||
QString browsercommand = ui->browserCommandLineEdit->text();
|
||||
bool fixedport = ui->chkFixedPort->isChecked();
|
||||
int portnumber = ui->spinPortNumber->value();
|
||||
bool opentabatstartup = ui->chkOpenTabAtStartup->isChecked();
|
||||
QString pythonpath = ui->pythonPathLineEdit->text();
|
||||
QString applicationpath = ui->applicationPathLineEdit->text();
|
||||
|
||||
if (fixedport && (settings.value("FixedPort").toBool() != fixedport ||
|
||||
settings.value("PortNumber").toInt() != portnumber) && isPortInUse(portnumber))
|
||||
{
|
||||
QString error = QString(QWidget::tr("The specified fixed port is already in use. Please provide any other valid port."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_needRestart = (settings.value("FixedPort").toBool() != fixedport ||
|
||||
settings.value("PortNumber").toInt() != portnumber ||
|
||||
settings.value("PythonPath").toString() != pythonpath ||
|
||||
settings.value("ApplicationPath").toString() != applicationpath);
|
||||
|
||||
settings.setValue("BrowserCommand", browsercommand);
|
||||
settings.setValue("FixedPort", fixedport);
|
||||
settings.setValue("PortNumber", portnumber);
|
||||
settings.setValue("OpenTabAtStartup", opentabatstartup);
|
||||
settings.setValue("PythonPath", pythonpath);
|
||||
settings.setValue("ApplicationPath", applicationpath);
|
||||
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
emit accepted(m_needRestart);
|
||||
emit closing(true);
|
||||
|
||||
this->close();
|
||||
}
|
||||
|
||||
void ConfigWindow::on_buttonBox_rejected()
|
||||
{
|
||||
emit closing(false);
|
||||
this->close();
|
||||
}
|
||||
|
||||
void ConfigWindow::on_chkFixedPort_stateChanged(int state)
|
||||
{
|
||||
if (state == Qt::Checked)
|
||||
ui->spinPortNumber->setEnabled(true);
|
||||
else
|
||||
ui->spinPortNumber->setEnabled(false);
|
||||
}
|
||||
|
||||
bool ConfigWindow::isPortInUse(const quint16 port) const
|
||||
{
|
||||
QTcpSocket socket;
|
||||
|
||||
// Bind the socket on the specified port.
|
||||
socket.bind(port, QTcpSocket::DontShareAddress);
|
||||
|
||||
// Returns the host port number of the local socket if available; otherwise returns 0
|
||||
quint16 tmpPort = socket.localPort();
|
||||
if (tmpPort == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ConfigWindow.h - Configuration window
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef CONFIGWINDOW_H
|
||||
#define CONFIGWINDOW_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class ConfigWindow;
|
||||
}
|
||||
|
||||
class ConfigWindow : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigWindow(QWidget *parent = Q_NULLPTR);
|
||||
void setConfigValues();
|
||||
|
||||
signals:
|
||||
void accepted(bool needRestart);
|
||||
void closing(bool accepted);
|
||||
|
||||
private slots:
|
||||
void on_buttonBox_accepted();
|
||||
void on_buttonBox_rejected();
|
||||
void on_chkFixedPort_stateChanged(int state);
|
||||
|
||||
private:
|
||||
Ui::ConfigWindow *ui;
|
||||
bool m_needRestart;
|
||||
|
||||
void initConfigWindow();
|
||||
bool isPortInUse(const quint16 port) const;
|
||||
};
|
||||
|
||||
#endif // CONFIGWINDOW_H
|
@ -1,652 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigWindow</class>
|
||||
<widget class="QDialog" name="ConfigWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>415</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>800</width>
|
||||
<height>415</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>pgAdmin 4 Configuration</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Runtime</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enter a command line to be used to start the browser. If blank, the system default browser will be used. %URL% will be replaced with the appropriate URL when executing the browser.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="pythonPathLabel_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browser Command</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="browserCommandLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>/usr/bin/firefox %URL%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>By default the runtime uses a random port number to ensure it can always run successfully. If you need to use a predictable port number, you can set one here. Note that if the port is already in use, the application will be unable to start.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Fixed Port Number?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chkFixedPort">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Port number</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinPortNumber">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>5050</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>By default, when the pgAdmin server is started a browser tab will be automatically opened to display the user interface. Un-check this option to run the server without automatically opening the browser.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open a Browser Window/Tab at Startup?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chkOpenTabAtStartup">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Python</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<spacer name="verticalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>13</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>The options below are intended for expert users only, and may not behave as expected as they modify fixed search paths and are not alternate values. Modify with care!</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>17</width>
|
||||
<height>13</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="pythonPathLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Python Path</string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="pythonPathLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>/usr/pgadmin4/lib/python3.8;/usr/pgadmin4/lib/python3.8/site-packages</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enter a PYTHONPATH if desired. Path elements should be semi-colon delimited.</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>13</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="applicationPathLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Application Path</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="applicationPathLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>/usr/pgadmin4/web</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enter the path to the directory containing pgAdmin.py if desired.</string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_8">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ConfigWindow</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ConfigWindow</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>chkFixedPort</sender>
|
||||
<signal>stateChanged(int)</signal>
|
||||
<receiver>ConfigWindow</receiver>
|
||||
<slot>on_chkFixedPort_stateChanged(int)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>202</x>
|
||||
<y>213</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>365</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>on_chkFixedPort_stateChanged(int)</slot>
|
||||
</slots>
|
||||
</ui>
|
@ -1,147 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// FloatingWindow.cpp - For GNOME 3.26 and above floating window will be used.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "FloatingWindow.h"
|
||||
#include "ui_FloatingWindow.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
|
||||
|
||||
FloatingWindow::FloatingWindow(QWidget *parent) :
|
||||
QMainWindow(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool FloatingWindow::Init()
|
||||
{
|
||||
ui = new Ui::FloatingWindow;
|
||||
ui->setupUi(this);
|
||||
|
||||
// Creating Menu
|
||||
createMenu();
|
||||
|
||||
// Setup the icon itself. For convenience, we'll also use it for the dialogue.
|
||||
#ifdef Q_OS_MAC
|
||||
QIcon icon(":pgAdmin4-mac.png");
|
||||
#else
|
||||
QIcon icon(":pgAdmin4.png");
|
||||
#endif
|
||||
|
||||
setWindowIcon(icon);
|
||||
setWindowTitle(tr("pgAdmin"));
|
||||
setFixedSize(300, 230);
|
||||
setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Create the menu
|
||||
void FloatingWindow::createMenu()
|
||||
{
|
||||
createActions();
|
||||
|
||||
m_floatingWindowMenu = menuBar()->addMenu(tr("&pgAdmin 4"));
|
||||
m_floatingWindowMenu->addAction(m_newAction);
|
||||
m_floatingWindowMenu->addAction(m_copyUrlAction);
|
||||
m_floatingWindowMenu->addSeparator();
|
||||
m_floatingWindowMenu->addAction(m_configAction);
|
||||
m_floatingWindowMenu->addAction(m_logAction);
|
||||
m_floatingWindowMenu->addSeparator();
|
||||
m_floatingWindowMenu->addAction(m_quitAction);
|
||||
}
|
||||
|
||||
|
||||
// Create the menu actions
|
||||
void FloatingWindow::createActions()
|
||||
{
|
||||
m_newAction = new QAction(tr("&New pgAdmin 4 window..."), this);
|
||||
m_newAction->setEnabled(false);
|
||||
connect(m_newAction, SIGNAL(triggered()), m_menuActions, SLOT(onNew()));
|
||||
|
||||
m_copyUrlAction = new QAction(tr("&Copy server URL"), this);
|
||||
m_copyUrlAction->setEnabled(false);
|
||||
connect(m_copyUrlAction, SIGNAL(triggered()), m_menuActions, SLOT(onCopyUrl()));
|
||||
|
||||
m_configAction = new QAction(tr("C&onfigure..."), this);
|
||||
m_configAction->setEnabled(false);
|
||||
connect(m_configAction, SIGNAL(triggered()), m_menuActions, SLOT(onConfig()));
|
||||
|
||||
m_logAction = new QAction(tr("&View log..."), this);
|
||||
m_logAction->setEnabled(false);
|
||||
connect(m_logAction, SIGNAL(triggered()), m_menuActions, SLOT(onLog()));
|
||||
|
||||
m_quitAction = new QAction(tr("&Shut down server"), this);
|
||||
m_quitAction->setEnabled(false);
|
||||
connect(m_quitAction, SIGNAL(triggered()), m_menuActions, SLOT(onQuit()));
|
||||
}
|
||||
|
||||
|
||||
void FloatingWindow::enablePostStartOptions()
|
||||
{
|
||||
if (m_newAction != Q_NULLPTR)
|
||||
m_newAction->setEnabled(true);
|
||||
|
||||
if (m_copyUrlAction != Q_NULLPTR)
|
||||
m_copyUrlAction->setEnabled(true);
|
||||
|
||||
if (m_configAction != Q_NULLPTR)
|
||||
m_configAction->setEnabled(true);
|
||||
|
||||
if (m_logAction != Q_NULLPTR)
|
||||
m_logAction->setEnabled(true);
|
||||
|
||||
if (m_quitAction != Q_NULLPTR)
|
||||
m_quitAction->setEnabled(true);
|
||||
}
|
||||
|
||||
void FloatingWindow::setMenuActions(MenuActions * menuActions)
|
||||
{
|
||||
m_menuActions = menuActions;
|
||||
}
|
||||
|
||||
// Enable the View Log option
|
||||
void FloatingWindow::enableViewLogOption()
|
||||
{
|
||||
if (m_logAction != Q_NULLPTR)
|
||||
m_logAction->setEnabled(true);
|
||||
}
|
||||
|
||||
// Disable the View Log option
|
||||
void FloatingWindow::disableViewLogOption()
|
||||
{
|
||||
if (m_logAction != Q_NULLPTR)
|
||||
m_logAction->setEnabled(false);
|
||||
}
|
||||
|
||||
// Enable the configure option
|
||||
void FloatingWindow::enableConfigOption()
|
||||
{
|
||||
if (m_configAction != Q_NULLPTR)
|
||||
m_configAction->setEnabled(true);
|
||||
}
|
||||
|
||||
// Disable the configure option
|
||||
void FloatingWindow::disableConfigOption()
|
||||
{
|
||||
if (m_configAction != Q_NULLPTR)
|
||||
m_configAction->setEnabled(false);
|
||||
}
|
||||
|
||||
void FloatingWindow::closeEvent(QCloseEvent * event)
|
||||
{
|
||||
// Emit the signal to shut down the python server.
|
||||
emit shutdownSignal(m_menuActions->getAppServerUrl());
|
||||
event->accept();
|
||||
exit(0);
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// FloatingWindow.h - For GNOME 3.26 and above floating window will be used.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef FLOATINGWINDOW_H
|
||||
#define FLOATINGWINDOW_H
|
||||
|
||||
#include "MenuActions.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
namespace Ui {
|
||||
class FloatingWindow;
|
||||
}
|
||||
|
||||
class FloatingWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FloatingWindow(QWidget *parent = Q_NULLPTR);
|
||||
|
||||
bool Init();
|
||||
void enablePostStartOptions();
|
||||
void enableViewLogOption();
|
||||
void disableViewLogOption();
|
||||
void enableConfigOption();
|
||||
void disableConfigOption();
|
||||
void setMenuActions(MenuActions * menuActions);
|
||||
|
||||
private:
|
||||
Ui::FloatingWindow *ui;
|
||||
|
||||
void createMenu();
|
||||
void createActions();
|
||||
void closeEvent(QCloseEvent * event);
|
||||
|
||||
QAction *m_newAction = Q_NULLPTR;
|
||||
QAction *m_copyUrlAction = Q_NULLPTR;
|
||||
QAction *m_configAction = Q_NULLPTR;
|
||||
QAction *m_logAction = Q_NULLPTR;
|
||||
QAction *m_quitAction = Q_NULLPTR;
|
||||
|
||||
QMenu *m_floatingWindowMenu = Q_NULLPTR;
|
||||
MenuActions *m_menuActions = Q_NULLPTR;
|
||||
|
||||
signals:
|
||||
void shutdownSignal(QUrl);
|
||||
};
|
||||
|
||||
#endif // FLOATINGWINDOW_H
|
@ -1,155 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FloatingWindow</class>
|
||||
<widget class="QMainWindow" name="FloatingWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>230</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>pgAdmin 4</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>281</width>
|
||||
<height>201</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border-image:url(":pgAdmin4.png");</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Note: Installing a system tray plugin will prevent this window being shown.</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
|
||||
<plist version="0.9">
|
||||
<dict>
|
||||
<!-- start of standard entries -->
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>pgAdmin 4</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.30.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>4.30.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.10</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright (C) 2013 - 2021, The pgAdmin Development Team</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>@ICON@</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>pgAdmin 4 - PostgreSQL Tools</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>@TYPEINFO@</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>@EXECUTABLE@</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.pgadmin.@EXECUTABLE@</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
@ -1,103 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// LogWindow.cpp - Log viewer window
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "LogWindow.h"
|
||||
#include "ui_LogWindow.h"
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QTime>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
LogWindow::LogWindow(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
initLogWindow();
|
||||
}
|
||||
|
||||
void LogWindow::initLogWindow()
|
||||
{
|
||||
ui = new Ui::LogWindow;
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
void LogWindow::LoadLog()
|
||||
{
|
||||
int startupLines;
|
||||
int serverLines;
|
||||
|
||||
ui->lblStatus->setText(tr("Loading logfiles..."));
|
||||
|
||||
ui->lblStartupLog->setText(tr("Startup Log (%1):").arg(g_startupLogFile));
|
||||
ui->lblServerLog->setText(tr("Server Log (%1):").arg(g_serverLogFile));
|
||||
|
||||
startupLines = this->readLog(g_startupLogFile, ui->textStartupLog);
|
||||
serverLines = this->readLog(g_serverLogFile, ui->textServerLog);
|
||||
|
||||
ui->lblStatus->setText(QString(tr("Loaded startup log (%1 lines) and server log (%2 lines).")).arg(startupLines).arg(serverLines));
|
||||
}
|
||||
|
||||
|
||||
void LogWindow::reload()
|
||||
{
|
||||
this->LoadLog();
|
||||
}
|
||||
|
||||
|
||||
// Read the logfile
|
||||
int LogWindow::readLog(QString logFile, QPlainTextEdit *logWidget)
|
||||
{
|
||||
FILE *log;
|
||||
char *buffer;
|
||||
long len = 0;
|
||||
int i;
|
||||
int lines = 0;
|
||||
|
||||
// Look busy!
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
this->setDisabled(true);
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 100 );
|
||||
|
||||
logWidget->clear();
|
||||
|
||||
// Attempt to open the file
|
||||
log = fopen(logFile.toUtf8().data(), "r");
|
||||
if (log == Q_NULLPTR)
|
||||
{
|
||||
logWidget->setPlainText(QString(tr("The log file (%1) could not be opened.")).arg(g_serverLogFile));
|
||||
this->setDisabled(false);
|
||||
QApplication::restoreOverrideCursor();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the file size, and read the data
|
||||
fseek(log, 0, SEEK_END);
|
||||
len = ftell(log);
|
||||
rewind(log);
|
||||
buffer = static_cast<char *>(malloc((len + 1) * sizeof(char)));
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (fread(buffer + i, 1, 1, log) > 0 && buffer[i] == '\n')
|
||||
lines++;
|
||||
}
|
||||
|
||||
buffer[i] = 0;
|
||||
|
||||
fclose(log);
|
||||
logWidget->setPlainText(buffer);
|
||||
|
||||
// And... relax
|
||||
this->setDisabled(false);
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
return lines;
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// LogWindow.h - Log viewer window
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef LOGWINDOW_H
|
||||
#define LOGWINDOW_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
namespace Ui {
|
||||
class LogWindow;
|
||||
}
|
||||
|
||||
class LogWindow : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LogWindow(QWidget *parent = Q_NULLPTR);
|
||||
void LoadLog();
|
||||
|
||||
private slots:
|
||||
void reload();
|
||||
|
||||
private:
|
||||
Ui::LogWindow *ui;
|
||||
|
||||
void initLogWindow();
|
||||
int readLog(QString logFile, QPlainTextEdit *logWidget);
|
||||
};
|
||||
|
||||
#endif // LOGWINDOW_H
|
@ -1,138 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LogWindow</class>
|
||||
<widget class="QDialog" name="LogWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>pgAdmin 4 Log</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblStartupLog">
|
||||
<property name="text">
|
||||
<string>Startup Log:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="textStartupLog">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Courier</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="centerOnScroll">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblServerLog">
|
||||
<property name="text">
|
||||
<string>Server Log:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="textServerLog">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Courier</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="centerOnScroll">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnReload">
|
||||
<property name="text">
|
||||
<string>Reload</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblStatus">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnClose">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>btnReload</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>LogWindow</receiver>
|
||||
<slot>reload()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>53</x>
|
||||
<y>471</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
<y>249</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>btnClose</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>LogWindow</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>731</x>
|
||||
<y>471</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
<y>249</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>reload()</slot>
|
||||
</slots>
|
||||
</ui>
|
@ -1,63 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Logger.cpp - Logger Utility
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QTextStream>
|
||||
#include <QStandardPaths>
|
||||
|
||||
Logger* Logger::m_pThis = Q_NULLPTR;
|
||||
QFile* Logger::m_Logfile = Q_NULLPTR;
|
||||
|
||||
Logger::Logger()
|
||||
{
|
||||
}
|
||||
|
||||
Logger::~Logger()
|
||||
{
|
||||
}
|
||||
|
||||
Logger* Logger::GetLogger()
|
||||
{
|
||||
if (m_pThis == Q_NULLPTR)
|
||||
{
|
||||
m_pThis = new Logger();
|
||||
m_Logfile = new QFile;
|
||||
m_Logfile->setFileName(g_startupLogFile);
|
||||
m_Logfile->open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
m_Logfile->setPermissions(QFile::ReadOwner|QFile::WriteOwner);
|
||||
}
|
||||
|
||||
return m_pThis;
|
||||
}
|
||||
|
||||
void Logger::Log(const QString& sMessage) const
|
||||
{
|
||||
QString text = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss: ") + sMessage + "\n";
|
||||
if (m_Logfile != Q_NULLPTR)
|
||||
{
|
||||
QTextStream out(m_Logfile);
|
||||
out << text;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::ReleaseLogger()
|
||||
{
|
||||
if (m_pThis != Q_NULLPTR)
|
||||
{
|
||||
if(m_Logfile != Q_NULLPTR)
|
||||
m_Logfile->close();
|
||||
delete m_pThis;
|
||||
m_pThis = Q_NULLPTR;
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Logger.h - Logger Utility
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QFile>
|
||||
|
||||
class Logger : public QObject
|
||||
{
|
||||
public:
|
||||
static Logger* GetLogger();
|
||||
static void ReleaseLogger();
|
||||
void Log(const QString& sMessage) const;
|
||||
|
||||
private:
|
||||
Logger();
|
||||
virtual ~Logger();
|
||||
|
||||
static Logger* m_pThis;
|
||||
static QFile *m_Logfile;
|
||||
};
|
||||
|
||||
#endif // LOGGER_H
|
@ -1,118 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// MenuActions.cpp - Common file for menu actions.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "MenuActions.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QEventLoop>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QSettings>
|
||||
|
||||
MenuActions::MenuActions()
|
||||
{
|
||||
}
|
||||
|
||||
void MenuActions::setAppServerUrl(QString appServerUrl)
|
||||
{
|
||||
m_appServerUrl = appServerUrl;
|
||||
}
|
||||
|
||||
|
||||
// Create a new application browser window on user request
|
||||
void MenuActions::onNew() const
|
||||
{
|
||||
QSettings settings;
|
||||
QString cmd = settings.value("BrowserCommand").toString();
|
||||
|
||||
if (!cmd.isEmpty())
|
||||
{
|
||||
cmd.replace("%URL%", m_appServerUrl);
|
||||
QProcess::startDetached(cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!QDesktopServices::openUrl(m_appServerUrl))
|
||||
{
|
||||
QString error(QWidget::tr("Failed to open the system default web browser. Is one installed?."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Copy the application server URL to the clipboard
|
||||
void MenuActions::onCopyUrl() const
|
||||
{
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(m_appServerUrl);
|
||||
}
|
||||
|
||||
|
||||
// Show the config dialogue
|
||||
void MenuActions::onConfig()
|
||||
{
|
||||
if (!m_configWindow)
|
||||
m_configWindow = new ConfigWindow();
|
||||
|
||||
m_configWindow->setConfigValues();
|
||||
m_configWindow->show();
|
||||
m_configWindow->raise();
|
||||
m_configWindow->activateWindow();
|
||||
connect(m_configWindow, SIGNAL(accepted(bool)), this, SLOT(onConfigDone(bool)));
|
||||
}
|
||||
|
||||
|
||||
void MenuActions::onConfigDone(bool needRestart) const
|
||||
{
|
||||
if (needRestart && QMessageBox::Yes == QMessageBox::question(Q_NULLPTR,
|
||||
tr("Shut down server?"),
|
||||
tr("The pgAdmin 4 server must be restarted for changes to take effect. Do you want to shut down the server now?"),
|
||||
QMessageBox::Yes | QMessageBox::No))
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Show the log window
|
||||
void MenuActions::onLog()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
if (!m_logWindow)
|
||||
m_logWindow = new LogWindow();
|
||||
|
||||
m_logWindow->show();
|
||||
m_logWindow->raise();
|
||||
m_logWindow->activateWindow();
|
||||
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
|
||||
|
||||
m_logWindow->LoadLog();
|
||||
}
|
||||
|
||||
|
||||
// Exit
|
||||
void MenuActions::onQuit()
|
||||
{
|
||||
if (QMessageBox::Yes == QMessageBox::question(Q_NULLPTR, tr("Shut down server?"), tr("Are you sure you want to shut down the pgAdmin 4 server?"), QMessageBox::Yes | QMessageBox::No))
|
||||
{
|
||||
// Emit the signal to shut down the python server.
|
||||
emit shutdownSignal(m_appServerUrl);
|
||||
QApplication::quit();
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// MenuActions.h - Common file for menu actions.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef MENUACTIONS_H
|
||||
#define MENUACTIONS_H
|
||||
|
||||
#include "LogWindow.h"
|
||||
#include "ConfigWindow.h"
|
||||
|
||||
class MenuActions: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MenuActions();
|
||||
|
||||
void setAppServerUrl(QString appServerUrl);
|
||||
QString getAppServerUrl() const { return m_appServerUrl; }
|
||||
|
||||
private:
|
||||
QString m_appServerUrl = "";
|
||||
LogWindow *m_logWindow = Q_NULLPTR;
|
||||
ConfigWindow *m_configWindow = Q_NULLPTR;
|
||||
|
||||
public slots:
|
||||
void onConfigDone(bool needRestart) const;
|
||||
|
||||
protected slots:
|
||||
void onNew() const;
|
||||
void onCopyUrl() const;
|
||||
void onConfig();
|
||||
void onLog();
|
||||
void onQuit();
|
||||
|
||||
signals:
|
||||
void shutdownSignal(QUrl);
|
||||
};
|
||||
|
||||
#endif // MENUACTIONS_H
|
@ -1,660 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Runtime.cpp - Core of the runtime
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "Runtime.h"
|
||||
#include "Server.h"
|
||||
#include "TrayIcon.h"
|
||||
#include "MenuActions.h"
|
||||
#include "ConfigWindow.h"
|
||||
#include "FloatingWindow.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "macos.h"
|
||||
#endif
|
||||
|
||||
// Must be before QT
|
||||
#include <Python.h>
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QTime>
|
||||
|
||||
|
||||
Runtime::Runtime()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool Runtime::go(int argc, char *argv[])
|
||||
{
|
||||
// Before starting main application, need to set 'QT_X11_NO_MITSHM=1'
|
||||
// to make the runtime work with IBM PPC machines.
|
||||
#if defined (Q_OS_LINUX)
|
||||
QByteArray val("1");
|
||||
qputenv("QT_X11_NO_MITSHM", val);
|
||||
#endif
|
||||
|
||||
// Create the QT application
|
||||
QApplication app(argc, argv);
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
|
||||
// Setup look n feel
|
||||
setupStyling(&app);
|
||||
|
||||
// Setup the settings management
|
||||
QCoreApplication::setOrganizationName("pgadmin");
|
||||
QCoreApplication::setOrganizationDomain("pgadmin.org");
|
||||
QCoreApplication::setApplicationName("pgadmin4");
|
||||
|
||||
QSettings settings;
|
||||
|
||||
// Interlock
|
||||
if (alreadyRunning())
|
||||
exit(0);
|
||||
|
||||
// Proxy config
|
||||
configureProxy();
|
||||
|
||||
// Display the spash screen
|
||||
m_splash = displaySplash(&app);
|
||||
|
||||
// Generate a random key to authenticate the client to the server
|
||||
QString key = QUuid::createUuid().toString();
|
||||
key = key.mid(1, key.length() - 2);
|
||||
|
||||
// Create Menu Actions
|
||||
MenuActions *menuActions = new MenuActions();
|
||||
|
||||
// Create the control object (tray icon or floating window
|
||||
m_splash->showMessage(QString(QWidget::tr("Checking for system tray...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable())
|
||||
m_trayIcon = createTrayIcon(menuActions);
|
||||
else
|
||||
m_floatingWindow = createFloatingWindow(menuActions);
|
||||
|
||||
// Fire up the app server
|
||||
const Server *server = startServerLoop(key);
|
||||
|
||||
// Ensure we'll cleanup
|
||||
QObject::connect(server, SIGNAL(finished()), server, SLOT(deleteLater()));
|
||||
atexit(cleanup);
|
||||
|
||||
// Generate the app server URL
|
||||
QString url = QString("http://127.0.0.1:%1/?key=%2").arg(m_port).arg(key);
|
||||
Logger::GetLogger()->Log(QString(QWidget::tr("Application Server URL: %1")).arg(url));
|
||||
|
||||
// Check the server is running
|
||||
checkServer(url);
|
||||
|
||||
// Stash the URL for any duplicate processes to open
|
||||
createAddressFile(url);
|
||||
|
||||
// Go!
|
||||
menuActions->setAppServerUrl(url);
|
||||
|
||||
// Enable the shutdown server menu as server started successfully.
|
||||
if (m_trayIcon != Q_NULLPTR)
|
||||
m_trayIcon->enablePostStartOptions();
|
||||
if (m_floatingWindow != Q_NULLPTR)
|
||||
m_floatingWindow->enablePostStartOptions();
|
||||
|
||||
// Open the browser if needed
|
||||
if (settings.value("OpenTabAtStartup", true).toBool())
|
||||
openBrowserTab(url);
|
||||
|
||||
// Make sure the server is shutdown if the server is quit by the user
|
||||
QObject::connect(menuActions, SIGNAL(shutdownSignal(QUrl)), server, SLOT(shutdown(QUrl)));
|
||||
|
||||
// Final cleanup
|
||||
m_splash->finish(Q_NULLPTR);
|
||||
|
||||
if (m_floatingWindow != Q_NULLPTR)
|
||||
m_floatingWindow->show();
|
||||
|
||||
Logger::GetLogger()->Log("Everything works fine, successfully started pgAdmin4.");
|
||||
Logger::ReleaseLogger();
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
||||
// Setup the styling
|
||||
void Runtime::setupStyling(QApplication *app) const
|
||||
{
|
||||
// Setup the styling
|
||||
#ifndef Q_OS_LINUX
|
||||
QFile stylesheet;
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
QSettings registry("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", QSettings::Registry64Format);
|
||||
if (!registry.value("AppsUseLightTheme", true).toBool())
|
||||
{
|
||||
qDebug( "Windows Dark Mode..." );
|
||||
stylesheet.setFileName(":/qdarkstyle/style.qss");
|
||||
stylesheet.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream stream(&stylesheet);
|
||||
app->setStyleSheet(stream.readAll());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
if (IsDarkMode())
|
||||
{
|
||||
qDebug( "macOS Dark Mode...");
|
||||
stylesheet.setFileName(":/qdarkstyle/style.qss");
|
||||
stylesheet.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream stream(&stylesheet);
|
||||
app->setStyleSheet(stream.readAll());
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Set high DPI pixmap to display icons clear on Qt widget.
|
||||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
}
|
||||
|
||||
// Check if we're already running. If we are, open a new browser tab.
|
||||
bool Runtime::alreadyRunning()
|
||||
{
|
||||
// Create a system-wide semaphore keyed by app name, exe hash and the username
|
||||
// to ensure instances are unique to the user and path
|
||||
QString userName = qgetenv("USER"); // *nix
|
||||
if (userName.isEmpty())
|
||||
userName = qgetenv("USERNAME"); // Windows
|
||||
|
||||
QString semaName = QString("pgadmin4-%1-%2-sema").arg(userName).arg(getExeHash());
|
||||
QString shmemName = QString("pgadmin4-%1-%2-shmem").arg(userName).arg(getExeHash());
|
||||
qDebug() << "Semaphore name:" << semaName;
|
||||
qDebug() << "Shared memory segment name:" << shmemName;
|
||||
|
||||
QSystemSemaphore sema(semaName, 1);
|
||||
sema.acquire();
|
||||
|
||||
#ifndef Q_OS_WIN32
|
||||
// We may need to clean up stale shmem segments on *nix. Attaching and detaching
|
||||
// should remove the segment if it is orphaned.
|
||||
QSharedMemory stale_shmem(shmemName);
|
||||
if (stale_shmem.attach())
|
||||
stale_shmem.detach();
|
||||
#endif
|
||||
|
||||
m_shmem = new QSharedMemory(shmemName);
|
||||
bool is_running;
|
||||
if (m_shmem->attach())
|
||||
is_running = true;
|
||||
else
|
||||
{
|
||||
m_shmem->create(1);
|
||||
is_running = false;
|
||||
}
|
||||
sema.release();
|
||||
|
||||
if (is_running)
|
||||
{
|
||||
QFile addressFile(g_addressFile);
|
||||
addressFile.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
QTextStream in(&addressFile);
|
||||
QString url = in.readLine();
|
||||
|
||||
qDebug() << "Already running. Opening browser tab to: " << url << "and exiting.";
|
||||
openBrowserTab(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Runtime::configureProxy() const
|
||||
{
|
||||
// In windows and linux, it is required to set application level proxy
|
||||
// because socket bind logic to find free port gives socket creation error
|
||||
// when system proxy is configured. We are also setting
|
||||
// "setUseSystemConfiguration"=true to use the system proxy which will
|
||||
// override this application level proxy. As this bug is fixed in Qt 5.9 so
|
||||
// need to set application proxy for Qt version < 5.9.
|
||||
//
|
||||
#if defined (Q_OS_WIN) && QT_VERSION <= 0x050800
|
||||
// Give dummy URL required to find proxy server configured in windows.
|
||||
QNetworkProxyQuery proxyQuery(QUrl("https://www.pgadmin.org"));
|
||||
QNetworkProxy l_proxy;
|
||||
QList<QNetworkProxy> listOfProxies = QNetworkProxyFactory::systemProxyForQuery(proxyQuery);
|
||||
|
||||
if (listOfProxies.size())
|
||||
{
|
||||
l_proxy = listOfProxies[0];
|
||||
|
||||
// If host name is not empty means proxy server is configured.
|
||||
if (!l_proxy.hostName().isEmpty()) {
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (Q_OS_LINUX) && QT_VERSION <= 0x050800
|
||||
QByteArray proxy_env;
|
||||
proxy_env = qgetenv("http_proxy");
|
||||
// If http_proxy environment is defined in linux then proxy server is configured.
|
||||
if (!proxy_env.isEmpty()) {
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Display the splash screen
|
||||
QSplashScreen * Runtime::displaySplash(QApplication *app)
|
||||
{
|
||||
QSplashScreen *splash = new QSplashScreen();
|
||||
splash->setPixmap(QPixmap(":/splash.png"));
|
||||
splash->setWindowFlags(splash->windowFlags() | Qt::WindowStaysOnTopHint);
|
||||
splash->show();
|
||||
app->processEvents(QEventLoop::AllEvents);
|
||||
|
||||
return splash;
|
||||
}
|
||||
|
||||
|
||||
// Get the port number we're going to use
|
||||
quint16 Runtime::getPort() const
|
||||
{
|
||||
quint16 port = 0L;
|
||||
QSettings settings;
|
||||
|
||||
if (settings.value("FixedPort", false).toBool())
|
||||
{
|
||||
// Use the fixed port number
|
||||
port = settings.value("PortNumber", 5050).toUInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find an unused port number. Essentially, we're just reserving one
|
||||
// here that Flask will use when we start up the server.
|
||||
QTcpSocket socket;
|
||||
|
||||
#if QT_VERSION >= 0x050900
|
||||
socket.setProxy(QNetworkProxy::NoProxy);
|
||||
#endif
|
||||
|
||||
socket.bind(0, QTcpSocket::ShareAddress);
|
||||
port = socket.localPort();
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
// Create a tray icon
|
||||
TrayIcon * Runtime::createTrayIcon(MenuActions *menuActions)
|
||||
{
|
||||
TrayIcon *trayIcon = Q_NULLPTR;
|
||||
|
||||
m_splash->showMessage(QString(QWidget::tr("Checking for system tray...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("Checking for system tray...");
|
||||
|
||||
// Start the tray service
|
||||
trayIcon = new TrayIcon();
|
||||
|
||||
// Set the MenuActions object to connect to slot
|
||||
if (trayIcon != Q_NULLPTR)
|
||||
trayIcon->setMenuActions(menuActions);
|
||||
|
||||
trayIcon->Init();
|
||||
|
||||
return trayIcon;
|
||||
}
|
||||
|
||||
|
||||
// Create a floating window
|
||||
FloatingWindow * Runtime::createFloatingWindow(MenuActions *menuActions)
|
||||
{
|
||||
FloatingWindow *floatingWindow = Q_NULLPTR;
|
||||
|
||||
m_splash->showMessage(QString(QWidget::tr("System tray not found, creating floating window...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("System tray not found, creating floating window...");
|
||||
floatingWindow = new FloatingWindow();
|
||||
if (floatingWindow == Q_NULLPTR)
|
||||
{
|
||||
QString error = QString(QWidget::tr("Unable to initialize either a tray icon or floating window."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set the MenuActions object to connect to slot
|
||||
floatingWindow->setMenuActions(menuActions);
|
||||
floatingWindow->Init();
|
||||
|
||||
return floatingWindow;
|
||||
}
|
||||
|
||||
|
||||
void Runtime::openConfigureWindow(const QString errorMsg)
|
||||
{
|
||||
m_splash->finish(Q_NULLPTR);
|
||||
|
||||
qDebug() << errorMsg;
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), errorMsg);
|
||||
Logger::GetLogger()->Log(errorMsg);
|
||||
|
||||
// Allow the user to tweak the configuration if needed
|
||||
m_configDone = false;
|
||||
QSettings settings;
|
||||
bool oldFixedPort = settings.value("FixedPort", false).toBool();
|
||||
|
||||
|
||||
ConfigWindow *dlg = new ConfigWindow();
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dlg->show();
|
||||
dlg->raise();
|
||||
dlg->activateWindow();
|
||||
QObject::connect(dlg, SIGNAL(closing(bool)), this, SLOT(onConfigDone(bool)));
|
||||
|
||||
// Wait for configuration to be completed
|
||||
while (!m_configDone)
|
||||
delay(100);
|
||||
|
||||
// Read the value of port again if user has changed.
|
||||
bool newFixedPort = settings.value("FixedPort", false).toBool();
|
||||
quint16 newPort = settings.value("PortNumber").toUInt();
|
||||
|
||||
// User hasn't changed the value of fixed port check box
|
||||
// only change the value of the port
|
||||
if (oldFixedPort == newFixedPort && newFixedPort && m_port != newPort)
|
||||
m_port = newPort;
|
||||
// User has selected the fixed port and it's old value is random port,
|
||||
// so port needs to be updated.
|
||||
else if (oldFixedPort != newFixedPort && newFixedPort)
|
||||
m_port = newPort;
|
||||
// User has deselect the fixed port and it's old value is fixed port,
|
||||
// so we will have to get the random port
|
||||
else if (oldFixedPort != newFixedPort && !newFixedPort)
|
||||
m_port = getPort();
|
||||
}
|
||||
|
||||
// Server startup loop
|
||||
Server * Runtime::startServerLoop(QString key)
|
||||
{
|
||||
bool done = false;
|
||||
Server *server;
|
||||
|
||||
// Get the port number to use
|
||||
m_port = getPort();
|
||||
|
||||
while (!done)
|
||||
{
|
||||
server = startServer(key);
|
||||
if (server == NULL)
|
||||
{
|
||||
Logger::ReleaseLogger();
|
||||
QApplication::quit();
|
||||
}
|
||||
|
||||
// Check for server startup errors
|
||||
if (server->isFinished() || server->getError().length() > 0)
|
||||
{
|
||||
QString error = QString(QWidget::tr("An error occurred initialising the pgAdmin 4 server:\n\n%1")).arg(server->getError());
|
||||
|
||||
delete server;
|
||||
|
||||
// Enable the View Log option for diagnostics
|
||||
if (m_floatingWindow)
|
||||
m_floatingWindow->enableViewLogOption();
|
||||
if (m_trayIcon)
|
||||
m_trayIcon->enableViewLogOption();
|
||||
|
||||
// Open the configuration window
|
||||
openConfigureWindow(error);
|
||||
|
||||
// Disable the View Log option again
|
||||
if (m_floatingWindow)
|
||||
m_floatingWindow->disableViewLogOption();
|
||||
if (m_trayIcon)
|
||||
m_trayIcon->disableViewLogOption();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Startup appears successful
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
// Slot called when re-configuration is done.
|
||||
void Runtime::onConfigDone(bool accepted)
|
||||
{
|
||||
if (accepted)
|
||||
m_configDone = true;
|
||||
else
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
// Start the server
|
||||
Server * Runtime::startServer(QString key)
|
||||
{
|
||||
Server *server;
|
||||
|
||||
m_splash->showMessage(QString(QWidget::tr("Starting pgAdmin4 server...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("Starting pgAdmin4 server...");
|
||||
|
||||
QString msg = QString(QWidget::tr("Creating server object, port:%1, key:%2, logfile:%3")).arg(m_port).arg(key).arg(g_serverLogFile);
|
||||
Logger::GetLogger()->Log(msg);
|
||||
server = new Server(this, m_port, key, g_serverLogFile);
|
||||
|
||||
Logger::GetLogger()->Log("Initializing server...");
|
||||
if (!server->Init())
|
||||
{
|
||||
m_splash->finish(Q_NULLPTR);
|
||||
|
||||
qDebug() << server->getError();
|
||||
|
||||
QString error = QString(QWidget::tr("An error occurred initialising the pgAdmin 4 server:\n\n%1")).arg(server->getError());
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Logger::GetLogger()->Log("Server initialized, starting server thread...");
|
||||
server->start();
|
||||
|
||||
// This is a hack to give the server a chance to start and potentially fail. As
|
||||
// the Python interpreter is a synchronous call, we can't check for proper startup
|
||||
// easily in a more robust way - we have to rely on a clean startup not returning.
|
||||
// It should always fail pretty quickly, and take longer to start if it succeeds, so
|
||||
// we don't really get a visible delay here.
|
||||
delay(1000);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
// Check the server is running properly
|
||||
void Runtime::checkServer(QString url)
|
||||
{
|
||||
// Read the server connection timeout from the registry or set the default timeout.
|
||||
QSettings settings;
|
||||
int timeout = settings.value("ConnectionTimeout", 90).toInt();
|
||||
|
||||
// Now the server should be up, we'll attempt to connect and get a response.
|
||||
// We'll retry in a loop a few time before aborting if necessary.
|
||||
|
||||
QTime endTime = QTime::currentTime().addSecs(timeout);
|
||||
QTime midTime1 = QTime::currentTime().addSecs(timeout/3);
|
||||
QTime midTime2 = QTime::currentTime().addSecs(timeout*2/3);
|
||||
bool alive = false;
|
||||
bool enableOptions = false;
|
||||
|
||||
Logger::GetLogger()->Log("The server should be up. Attempting to connect and get a response.");
|
||||
while(QTime::currentTime() <= endTime)
|
||||
{
|
||||
alive = pingServer(QUrl(url));
|
||||
|
||||
if (alive)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(QTime::currentTime() >= midTime1)
|
||||
{
|
||||
if (m_floatingWindow && !enableOptions)
|
||||
{
|
||||
m_floatingWindow->enableViewLogOption();
|
||||
m_floatingWindow->enableConfigOption();
|
||||
enableOptions = true;
|
||||
}
|
||||
|
||||
if (m_trayIcon && !enableOptions)
|
||||
{
|
||||
m_trayIcon->enableViewLogOption();
|
||||
m_trayIcon->enableConfigOption();
|
||||
enableOptions = true;
|
||||
}
|
||||
|
||||
if(QTime::currentTime() < midTime2) {
|
||||
m_splash->showMessage(QString(QWidget::tr("Taking longer than usual...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_splash->showMessage(QString(QWidget::tr("Almost there...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
}
|
||||
}
|
||||
|
||||
delay(200);
|
||||
}
|
||||
|
||||
// Attempt to connect one more time in case of a long network timeout while looping
|
||||
Logger::GetLogger()->Log("Attempt to connect one more time in case of a long network timeout while looping");
|
||||
if (!alive && !pingServer(QUrl(url)))
|
||||
{
|
||||
m_splash->finish(Q_NULLPTR);
|
||||
QString error(QWidget::tr("The pgAdmin 4 server could not be contacted."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create the address file
|
||||
void Runtime::createAddressFile(QString url) const
|
||||
{
|
||||
QFile addressFile(g_addressFile);
|
||||
if (addressFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
addressFile.setPermissions(QFile::ReadOwner|QFile::WriteOwner);
|
||||
QTextStream out(&addressFile);
|
||||
out << url << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Open a browser tab
|
||||
void Runtime::openBrowserTab(QString url) const
|
||||
{
|
||||
QSettings settings;
|
||||
QString cmd = settings.value("BrowserCommand").toString();
|
||||
|
||||
if (!cmd.isEmpty())
|
||||
{
|
||||
cmd.replace("%URL%", url);
|
||||
QProcess::startDetached(cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!QDesktopServices::openUrl(url))
|
||||
{
|
||||
QString error(QWidget::tr("Failed to open the system default web browser. Is one installed?."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Make a request to the Python API server
|
||||
QString Runtime::serverRequest(QUrl url, QString path)
|
||||
{
|
||||
QNetworkAccessManager manager;
|
||||
QEventLoop loop;
|
||||
QNetworkReply *reply;
|
||||
QVariant redirectUrl;
|
||||
|
||||
|
||||
url.setPath(path);
|
||||
QString requestUrl = url.toString();
|
||||
|
||||
do
|
||||
{
|
||||
reply = manager.get(QNetworkRequest(url));
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
|
||||
redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
url = redirectUrl.toUrl();
|
||||
|
||||
if (!redirectUrl.isNull())
|
||||
delete reply;
|
||||
|
||||
} while (!redirectUrl.isNull());
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
qDebug() << "Failed to connect to the server:" << reply->errorString() << "- request URL:" << requestUrl << ".";
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString response = reply->readAll();
|
||||
qDebug() << "Server response:" << response << "- request URL:" << requestUrl << ".";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
// Ping the application server to see if it's alive
|
||||
bool Runtime::pingServer(QUrl url)
|
||||
{
|
||||
return serverRequest(url, "/misc/ping") == "PING";
|
||||
}
|
||||
|
||||
|
||||
// Shutdown the application server
|
||||
bool Runtime::shutdownServer(QUrl url)
|
||||
{
|
||||
return serverRequest(url, "/misc/shutdown") == "SHUTDOWN";
|
||||
}
|
||||
|
||||
|
||||
void Runtime::delay(int milliseconds) const
|
||||
{
|
||||
QTime endTime = QTime::currentTime().addMSecs(milliseconds);
|
||||
while(QTime::currentTime() < endTime)
|
||||
{
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
|
||||
}
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Runtime.h - Core of the runtime
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef RUNTIME_H
|
||||
#define RUNTIME_H
|
||||
|
||||
// Include the Python header here as it needs to appear before any QT
|
||||
// headers anywhere in the app.
|
||||
#ifdef __MINGW32__
|
||||
#include <cmath>
|
||||
#endif
|
||||
#include <Python.h>
|
||||
|
||||
#include "TrayIcon.h"
|
||||
#include "MenuActions.h"
|
||||
#include "FloatingWindow.h"
|
||||
|
||||
// QT headers
|
||||
#include <QtWidgets>
|
||||
|
||||
class Server;
|
||||
|
||||
class Runtime: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Runtime();
|
||||
|
||||
bool alreadyRunning();
|
||||
bool go(int argc, char *argv[]);
|
||||
void delay(int milliseconds) const;
|
||||
bool shutdownServer(QUrl url);
|
||||
|
||||
private:
|
||||
QSharedMemory *m_shmem;
|
||||
bool m_configDone;
|
||||
FloatingWindow *m_floatingWindow = Q_NULLPTR;
|
||||
TrayIcon *m_trayIcon = Q_NULLPTR;
|
||||
QSplashScreen *m_splash = Q_NULLPTR;
|
||||
quint16 m_port = 0;
|
||||
|
||||
void setupStyling(QApplication *app) const;
|
||||
void configureProxy() const;
|
||||
QSplashScreen *displaySplash(QApplication *app);
|
||||
quint16 getPort() const;
|
||||
TrayIcon *createTrayIcon(MenuActions *menuActions);
|
||||
FloatingWindow *createFloatingWindow(MenuActions *menuActions);
|
||||
Server *startServerLoop(QString key);
|
||||
Server *startServer(QString key);
|
||||
void checkServer(QString url);
|
||||
void createAddressFile(QString url) const;
|
||||
void openBrowserTab(QString url) const;
|
||||
QString serverRequest(QUrl url, QString path);
|
||||
bool pingServer(QUrl url);
|
||||
void openConfigureWindow(const QString errorMsg);
|
||||
|
||||
private slots:
|
||||
void onConfigDone(bool accepted);
|
||||
};
|
||||
|
||||
#endif // RUNTIME_H
|
@ -1,363 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Server.cpp - Thread in which the web server will run.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
|
||||
// Must be before QT
|
||||
#include <Python.h>
|
||||
|
||||
#include "Server.h"
|
||||
#include "Logger.h"
|
||||
|
||||
// QT headers
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
|
||||
|
||||
static void add_to_path(QString &python_path, QString path, bool prepend=false)
|
||||
{
|
||||
if (!python_path.contains(path))
|
||||
{
|
||||
if (!prepend)
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
if (!python_path.isEmpty() && !python_path.endsWith(";"))
|
||||
python_path.append(";");
|
||||
#else
|
||||
if (!python_path.isEmpty() && !python_path.endsWith(":"))
|
||||
python_path.append(":");
|
||||
#endif
|
||||
|
||||
python_path.append(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
if (!python_path.isEmpty() && !python_path.startsWith(";"))
|
||||
python_path.prepend(";");
|
||||
#else
|
||||
if (!python_path.isEmpty() && !python_path.startsWith(":"))
|
||||
python_path.prepend(":");
|
||||
#endif
|
||||
|
||||
python_path.prepend(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Server::Server(Runtime *runtime, quint16 port, QString key, QString logFileName):
|
||||
m_runtime(runtime),
|
||||
m_port(port),
|
||||
m_key(key),
|
||||
m_logFileName(logFileName)
|
||||
{
|
||||
// Initialise Python
|
||||
Py_NoSiteFlag=1;
|
||||
Py_NoUserSiteDirectory=1;
|
||||
Py_DontWriteBytecodeFlag=1;
|
||||
|
||||
// Python3 requires conversion of char * to wchar_t *, so...
|
||||
const char *appName = QString("pgAdmin 4").toUtf8();
|
||||
const size_t cSize = strlen(appName)+1;
|
||||
m_wcAppName = new wchar_t[cSize];
|
||||
mbstowcs (m_wcAppName, appName, cSize);
|
||||
Py_SetProgramName(m_wcAppName);
|
||||
|
||||
// Setup the search path
|
||||
QSettings settings;
|
||||
QString python_path = settings.value("PythonPath").toString();
|
||||
|
||||
// Get the application directory
|
||||
QString app_dir = QCoreApplication::applicationDirPath();
|
||||
QString path_env = qgetenv("PATH");
|
||||
QString pythonHome;
|
||||
QStringList path_list;
|
||||
int i;
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// In the case we're running in a release appbundle, we need to ensure the
|
||||
// bundled virtual env is included in the Python path. We include it at the
|
||||
// end, so expert users can override the path, but we do not save it, because
|
||||
// if users move the app bundle, we'll end up with dead entries
|
||||
|
||||
// Build (and canonicalise) the virtual environment path
|
||||
QFileInfo venvBinPath(app_dir + "/../Resources/venv/bin");
|
||||
QFileInfo venvLibPath(app_dir + "/../Resources/venv/lib/python");
|
||||
QFileInfo venvDynLibPath(app_dir + "/../Resources/venv/lib/python/lib-dynload");
|
||||
QFileInfo venvSitePackagesPath(app_dir + "/../Resources/venv/lib/python/site-packages");
|
||||
QFileInfo venvPath(app_dir + "/../Resources/venv");
|
||||
|
||||
// Prepend the bin directory to the path
|
||||
add_to_path(path_env, venvBinPath.canonicalFilePath(), true);
|
||||
// Append the path, if it's not already there
|
||||
add_to_path(python_path, venvLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvDynLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvSitePackagesPath.canonicalFilePath());
|
||||
add_to_path(pythonHome, venvPath.canonicalFilePath());
|
||||
#elif defined(Q_OS_WIN)
|
||||
|
||||
// In the case we're running in a release application, we need to ensure the
|
||||
// bundled virtual env is included in the Python path. We include it at the
|
||||
// end, so expert users can override the path, but we do not save it.
|
||||
|
||||
// Build (and canonicalise) the virtual environment path
|
||||
QFileInfo venvBinPath(app_dir + "/../venv");
|
||||
QFileInfo venvLibPath(app_dir + "/../venv/Lib");
|
||||
QFileInfo venvDLLsPath(app_dir + "/../venv/DLLs");
|
||||
QFileInfo venvSitePackagesPath(app_dir + "/../venv/Lib/site-packages");
|
||||
QFileInfo venvPath(app_dir + "/../venv");
|
||||
|
||||
// Prepend the bin directory to the path
|
||||
add_to_path(path_env, venvBinPath.canonicalFilePath(), true);
|
||||
// Append paths, if they're not already there
|
||||
add_to_path(python_path, venvLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvDLLsPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvSitePackagesPath.canonicalFilePath());
|
||||
add_to_path(pythonHome, venvPath.canonicalFilePath());
|
||||
#else
|
||||
// Build (and canonicalise) the virtual environment path
|
||||
QFileInfo venvBinPath(app_dir + "/../venv/bin");
|
||||
QFileInfo venvLibPath(app_dir + "/../venv/lib/python");
|
||||
QFileInfo venvDynLibPath(app_dir + "/../venv/lib/python/lib-dynload");
|
||||
QFileInfo venvSitePackagesPath(app_dir + "/../venv/lib/python/site-packages");
|
||||
QFileInfo venvPath(app_dir + "/../venv");
|
||||
|
||||
// Prepend the bin directory to the path
|
||||
add_to_path(path_env, venvBinPath.canonicalFilePath(), true);
|
||||
// Append the path, if it's not already there
|
||||
add_to_path(python_path, venvLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvDynLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvSitePackagesPath.canonicalFilePath());
|
||||
add_to_path(pythonHome, venvPath.canonicalFilePath());
|
||||
#endif
|
||||
|
||||
qputenv("PATH", path_env.toUtf8().data());
|
||||
|
||||
if (python_path.length() > 0)
|
||||
{
|
||||
// Split the path setting into individual entries
|
||||
path_list = python_path.split(";", QString::SkipEmptyParts);
|
||||
python_path = QString();
|
||||
|
||||
// Add new additional path elements
|
||||
for (i = path_list.size() - 1; i >= 0 ; --i)
|
||||
{
|
||||
python_path.append(path_list.at(i));
|
||||
if (i > 0)
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
python_path.append(";");
|
||||
#else
|
||||
python_path.append(":");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
qputenv("PYTHONPATH", python_path.toUtf8().data());
|
||||
}
|
||||
|
||||
qDebug() << "Python path: " << python_path
|
||||
<< "\nPython Home: " << pythonHome;
|
||||
|
||||
Logger::GetLogger()->Log(QString("Python Path: %1").arg(python_path));
|
||||
Logger::GetLogger()->Log(QString("Python Home: %1").arg(pythonHome));
|
||||
|
||||
if (!pythonHome.isEmpty())
|
||||
{
|
||||
const char *python_home = pythonHome.toUtf8().data();
|
||||
const size_t home_size = strlen(python_home) + 1;
|
||||
m_wcPythonHome = new wchar_t[home_size];
|
||||
mbstowcs (m_wcPythonHome, python_home, home_size);
|
||||
|
||||
Py_SetPythonHome(m_wcPythonHome);
|
||||
}
|
||||
|
||||
Logger::GetLogger()->Log("Initializing Python...");
|
||||
Py_Initialize();
|
||||
Logger::GetLogger()->Log("Python initialized.");
|
||||
|
||||
// Get the current path
|
||||
PyObject* sysPath = PySys_GetObject(const_cast<char *>("path"));
|
||||
if (sysPath != Q_NULLPTR)
|
||||
{
|
||||
// Add new additional path elements
|
||||
Logger::GetLogger()->Log("Adding new additional path elements");
|
||||
for (i = path_list.size() - 1; i >= 0 ; --i)
|
||||
{
|
||||
PyList_Append(sysPath, PyUnicode_DecodeFSDefault(path_list.at(i).toUtf8().data()));
|
||||
}
|
||||
}
|
||||
else
|
||||
Logger::GetLogger()->Log("Unable to get the current path.");
|
||||
|
||||
// Redirect stderr
|
||||
Logger::GetLogger()->Log("Redirecting stderr...");
|
||||
PyObject *sys = PyImport_ImportModule("sys");
|
||||
if (sys != Q_NULLPTR)
|
||||
{
|
||||
PyObject *err = Q_NULLPTR;
|
||||
FILE *log = Q_NULLPTR;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
char *logFile = m_logFileName.toUtf8().data();
|
||||
size_t fileSize = strlen(logFile) + 1;
|
||||
wchar_t * wcLogFileName = new wchar_t[fileSize];
|
||||
mbstowcs (wcLogFileName, logFile, fileSize);
|
||||
|
||||
log = _wfopen(wcLogFileName, (wchar_t *)"w");
|
||||
#else
|
||||
log = fopen(m_logFileName.toUtf8().data(), const_cast<char *>("w"));
|
||||
#endif
|
||||
if (log != Q_NULLPTR)
|
||||
{
|
||||
int fd = fileno(log);
|
||||
err = PyFile_FromFd(fd, Q_NULLPTR, const_cast<char *>("w"), -1, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, 0);
|
||||
}
|
||||
else
|
||||
Logger::GetLogger()->Log(QString("Failed to open log file: %1").arg(m_logFileName));
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
if (wcLogFileName != NULL)
|
||||
{
|
||||
delete wcLogFileName;
|
||||
wcLogFileName = NULL;
|
||||
}
|
||||
#endif
|
||||
QFile(m_logFileName).setPermissions(QFile::ReadOwner|QFile::WriteOwner);
|
||||
if (err != Q_NULLPTR)
|
||||
{
|
||||
PyObject_SetAttrString(sys, "stderr", err);
|
||||
Logger::GetLogger()->Log("stderr redirected successfully.");
|
||||
}
|
||||
else
|
||||
Logger::GetLogger()->Log(QString("Failed to get the file pointer of: %1 ").arg(m_logFileName));
|
||||
}
|
||||
else
|
||||
Logger::GetLogger()->Log("Failed to import 'sys' module.");
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
if (m_wcAppName)
|
||||
delete m_wcAppName;
|
||||
|
||||
if (m_wcPythonHome)
|
||||
delete m_wcPythonHome;
|
||||
|
||||
// Shutdown Python
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
bool Server::Init()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
// Find the webapp
|
||||
QStringList paths;
|
||||
paths.append("../web/"); // Linux source tree
|
||||
paths.append("../../web/"); // Windows source tree
|
||||
paths.append("../../../../web/"); // Mac source tree (in a dev env)
|
||||
#ifdef Q_OS_MAC
|
||||
paths.append("../Resources/web/"); // Mac source tree (in a release app bundle)
|
||||
#endif
|
||||
paths.append(settings.value("ApplicationPath").toString()); // System configured value
|
||||
paths.append(""); // Should be last!
|
||||
|
||||
for (int i = 0; i < paths.size(); ++i)
|
||||
{
|
||||
QDir dir;
|
||||
|
||||
if (paths[i].startsWith('/'))
|
||||
dir.setPath(paths[i]);
|
||||
else
|
||||
dir.setPath(QCoreApplication::applicationDirPath() + "/" + paths[i]);
|
||||
|
||||
m_appfile = dir.canonicalPath() + "/pgAdmin4.py";
|
||||
|
||||
if (QFile::exists(m_appfile))
|
||||
{
|
||||
qDebug() << "Webapp path: " << m_appfile;
|
||||
Logger::GetLogger()->Log(QString("Webapp Path: %1").arg(m_appfile));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QFile::exists(m_appfile))
|
||||
{
|
||||
Logger::GetLogger()->Log("Failed to locate pgAdmin4.py, terminating server thread.");
|
||||
setError(tr("Failed to locate pgAdmin4.py, terminating server thread."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Server::run()
|
||||
{
|
||||
// Open the application code and run it.
|
||||
Logger::GetLogger()->Log("Open the application code and run it.");
|
||||
FILE *cp = fopen(m_appfile.toUtf8().data(), "r");
|
||||
if (!cp)
|
||||
{
|
||||
Logger::GetLogger()->Log(QString(tr("Failed to open the application file: %1, server thread exiting.")).arg(m_appfile));
|
||||
setError(QString(tr("Failed to open the application file: %1, server thread exiting.")).arg(m_appfile));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the port number and key, and force SERVER_MODE off.
|
||||
Logger::GetLogger()->Log("Set the port number, key and force SERVER_MODE off");
|
||||
PyRun_SimpleString(QString("PGADMIN_INT_PORT = %1").arg(m_port).toLatin1());
|
||||
PyRun_SimpleString(QString("PGADMIN_INT_KEY = '%1'").arg(m_key).toLatin1());
|
||||
PyRun_SimpleString(QString("SERVER_MODE = False").toLatin1());
|
||||
|
||||
// Run the app!
|
||||
QByteArray m_appfile_utf8 = m_appfile.toUtf8();
|
||||
|
||||
/*
|
||||
* Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later
|
||||
* versions, prepends an empty string to sys.path when the argv[0] argument does not contain a path separator,
|
||||
* which might allow local users to execute arbitrary code via a Trojan horse Python file in the current working directory.
|
||||
* Here we have to set arguments explicitly to python interpreter. Check more details in 'PySys_SetArgv' documentation.
|
||||
*/
|
||||
const char *appName = m_appfile_utf8.data();
|
||||
const size_t cSize = strlen(appName)+1;
|
||||
wchar_t* wcAppName = new wchar_t[cSize];
|
||||
mbstowcs (wcAppName, appName, cSize);
|
||||
wchar_t* n_argv[] = { wcAppName };
|
||||
PySys_SetArgv(1, n_argv);
|
||||
|
||||
Logger::GetLogger()->Log("PyRun_SimpleFile launching application server...");
|
||||
if (PyRun_SimpleFile(cp, m_appfile_utf8.data()) != 0)
|
||||
{
|
||||
Logger::GetLogger()->Log("Failed to launch the application server, server thread exiting.");
|
||||
setError(tr("Failed to launch the application server, server thread exiting."));
|
||||
}
|
||||
|
||||
fclose(cp);
|
||||
}
|
||||
|
||||
void Server::shutdown(QUrl url)
|
||||
{
|
||||
if (!m_runtime->shutdownServer(url))
|
||||
setError(tr("Failed to shut down application server thread."));
|
||||
|
||||
QThread::quit();
|
||||
QThread::wait();
|
||||
while(!this->isFinished())
|
||||
{
|
||||
Logger::GetLogger()->Log("Waiting for server to shut down.");
|
||||
m_runtime->delay(250);
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Server.h - Thread in which the web server will run.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include "Runtime.h"
|
||||
|
||||
#include <QThread>
|
||||
#include <QUrl>
|
||||
|
||||
class Server : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Server(Runtime *runtime, quint16 port, QString key, QString logFileName);
|
||||
~Server();
|
||||
|
||||
bool Init();
|
||||
QString getError() const { return m_error; }
|
||||
|
||||
public slots:
|
||||
void shutdown(QUrl url);
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
void setError(QString error) { m_error = error; }
|
||||
|
||||
QString m_appfile;
|
||||
QString m_error;
|
||||
|
||||
Runtime *m_runtime;
|
||||
quint16 m_port;
|
||||
QString m_key;
|
||||
QString m_logFileName;
|
||||
|
||||
// Application name in UTF-8 for Python
|
||||
wchar_t *m_wcAppName = Q_NULLPTR;
|
||||
|
||||
// PythonHome for Python
|
||||
wchar_t *m_wcPythonHome = Q_NULLPTR;
|
||||
};
|
||||
|
||||
#endif // SERVER_H
|
||||
|
@ -1,143 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// TrayIcon.cpp - Manages the tray icon
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "TrayIcon.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
|
||||
TrayIcon::TrayIcon()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void TrayIcon::Init()
|
||||
{
|
||||
createTrayIcon();
|
||||
|
||||
if (m_trayIcon)
|
||||
m_trayIcon->show();
|
||||
}
|
||||
|
||||
|
||||
// Create the tray icon
|
||||
void TrayIcon::createTrayIcon()
|
||||
{
|
||||
createActions();
|
||||
|
||||
if (m_trayIconMenu)
|
||||
{
|
||||
delete m_trayIconMenu;
|
||||
m_trayIconMenu = Q_NULLPTR;
|
||||
}
|
||||
|
||||
m_trayIconMenu = new QMenu(this);
|
||||
m_trayIconMenu->addAction(m_newAction);
|
||||
m_trayIconMenu->addAction(m_copyUrlAction);
|
||||
m_trayIconMenu->addSeparator();
|
||||
m_trayIconMenu->addAction(m_configAction);
|
||||
m_trayIconMenu->addAction(m_logAction);
|
||||
m_trayIconMenu->addSeparator();
|
||||
m_trayIconMenu->addAction(m_quitAction);
|
||||
|
||||
if (!m_trayIcon)
|
||||
m_trayIcon = new QSystemTrayIcon(this);
|
||||
|
||||
m_trayIcon->setContextMenu(m_trayIconMenu);
|
||||
|
||||
// Setup the icon itself. For convenience, we'll also use it for the dialogue.
|
||||
#ifdef Q_OS_MAC
|
||||
QIcon icon(":pgAdmin4-mac.png");
|
||||
#else
|
||||
QIcon icon(":pgAdmin4.png");
|
||||
#endif
|
||||
|
||||
m_trayIcon->setIcon(icon);
|
||||
setWindowIcon(icon);
|
||||
}
|
||||
|
||||
|
||||
// Create the menu actions
|
||||
void TrayIcon::createActions()
|
||||
{
|
||||
m_newAction = new QAction(tr("&New pgAdmin 4 window..."), this);
|
||||
m_newAction->setEnabled(false);
|
||||
connect(m_newAction, SIGNAL(triggered()), m_menuActions, SLOT(onNew()));
|
||||
|
||||
m_copyUrlAction = new QAction(tr("&Copy server URL"), this);
|
||||
m_copyUrlAction->setEnabled(false);
|
||||
connect(m_copyUrlAction, SIGNAL(triggered()), m_menuActions, SLOT(onCopyUrl()));
|
||||
|
||||
m_configAction = new QAction(tr("&Configure..."), this);
|
||||
m_configAction->setEnabled(false);
|
||||
connect(m_configAction, SIGNAL(triggered()), m_menuActions, SLOT(onConfig()));
|
||||
|
||||
m_logAction = new QAction(tr("&View log..."), this);
|
||||
m_logAction->setEnabled(false);
|
||||
connect(m_logAction, SIGNAL(triggered()), m_menuActions, SLOT(onLog()));
|
||||
|
||||
m_quitAction = new QAction(tr("&Shut down server"), this);
|
||||
m_quitAction->setEnabled(false);
|
||||
connect(m_quitAction, SIGNAL(triggered()), m_menuActions, SLOT(onQuit()));
|
||||
}
|
||||
|
||||
|
||||
void TrayIcon::enablePostStartOptions()
|
||||
{
|
||||
if (m_newAction != Q_NULLPTR)
|
||||
m_newAction->setEnabled(true);
|
||||
|
||||
if (m_copyUrlAction != Q_NULLPTR)
|
||||
m_copyUrlAction->setEnabled(true);
|
||||
|
||||
if (m_configAction != Q_NULLPTR)
|
||||
m_configAction->setEnabled(true);
|
||||
|
||||
if (m_logAction != Q_NULLPTR)
|
||||
m_logAction->setEnabled(true);
|
||||
|
||||
if (m_quitAction != Q_NULLPTR)
|
||||
m_quitAction->setEnabled(true);
|
||||
}
|
||||
|
||||
// Enable the View Log option
|
||||
void TrayIcon::enableViewLogOption()
|
||||
{
|
||||
if (m_logAction != Q_NULLPTR)
|
||||
m_logAction->setEnabled(true);
|
||||
}
|
||||
|
||||
// Disable the View Log option
|
||||
void TrayIcon::disableViewLogOption()
|
||||
{
|
||||
if (m_logAction != Q_NULLPTR)
|
||||
m_logAction->setEnabled(false);
|
||||
}
|
||||
|
||||
// Enable the configure option
|
||||
void TrayIcon::enableConfigOption()
|
||||
{
|
||||
if (m_configAction != Q_NULLPTR)
|
||||
m_configAction->setEnabled(true);
|
||||
}
|
||||
|
||||
// Disable the configure option
|
||||
void TrayIcon::disableConfigOption()
|
||||
{
|
||||
if (m_configAction != Q_NULLPTR)
|
||||
m_configAction->setEnabled(false);
|
||||
}
|
||||
|
||||
void TrayIcon::setMenuActions(MenuActions * menuActions)
|
||||
{
|
||||
m_menuActions = menuActions;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// TrayIcon.h - Manages the tray icon
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef TRAYICON_H
|
||||
#define TRAYICON_H
|
||||
|
||||
#include "MenuActions.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
class TrayIcon : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TrayIcon();
|
||||
|
||||
void Init();
|
||||
void enablePostStartOptions();
|
||||
void enableViewLogOption();
|
||||
void disableViewLogOption();
|
||||
void enableConfigOption();
|
||||
void disableConfigOption();
|
||||
void setMenuActions(MenuActions * menuActions);
|
||||
|
||||
private:
|
||||
void createTrayIcon();
|
||||
void createActions();
|
||||
|
||||
QAction *m_newAction = Q_NULLPTR;
|
||||
QAction *m_copyUrlAction = Q_NULLPTR;
|
||||
QAction *m_configAction = Q_NULLPTR;
|
||||
QAction *m_logAction = Q_NULLPTR;
|
||||
QAction *m_quitAction = Q_NULLPTR;
|
||||
|
||||
QSystemTrayIcon *m_trayIcon = Q_NULLPTR;
|
||||
QMenu *m_trayIconMenu = Q_NULLPTR;
|
||||
|
||||
MenuActions *m_menuActions = Q_NULLPTR;
|
||||
};
|
||||
|
||||
#endif // TRAYICON_H
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
1
runtime/assets/welcome_logo.svg
Normal file
After Width: | Height: | Size: 17 KiB |