Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d57072b156 | ||
|
|
5860de838a | ||
|
|
4b2b08174c | ||
|
|
2b64834179 | ||
|
|
0d754fc39f | ||
|
|
9c6c32e936 | ||
|
|
bcce574e83 | ||
|
|
5a132a547f | ||
|
|
9588d76b75 | ||
|
|
8ea85221f0 | ||
|
|
50a78abf48 | ||
|
|
739d8e2f1f | ||
|
|
f855ed88ce | ||
|
|
510bc6c974 | ||
|
|
cc10a4ee54 | ||
|
|
62c9376546 | ||
|
|
bcba596408 | ||
|
|
18dc938556 | ||
|
|
263e235862 | ||
|
|
354679e69b | ||
|
|
64159d11ce | ||
|
|
13665f68d8 | ||
|
|
2ddbddba33 | ||
|
|
fe757c384b | ||
|
|
62d7a1044e | ||
|
|
5ae681f2ba | ||
|
|
f0ad26ba7a | ||
|
|
bdc08d94c4 | ||
|
|
8bededa859 | ||
|
|
e64c52a446 | ||
|
|
5ebfd88f11 | ||
|
|
bc5a5a5b45 | ||
|
|
da40573f0b | ||
|
|
765e130336 | ||
|
|
a317df1351 | ||
|
|
026ef7b8b7 | ||
|
|
30e7016077 | ||
|
|
8150f93c06 | ||
|
|
c3ea3b7bdf | ||
|
|
9cfb2f3cbe | ||
|
|
55e14e4d78 | ||
|
|
788f7f3a69 | ||
|
|
e819465e38 | ||
|
|
ff79dfc186 | ||
|
|
3779c36d0f | ||
|
|
80ce889c3d | ||
|
|
5891bda06a | ||
|
|
120dbcc7d8 | ||
|
|
512afd6618 | ||
|
|
f79b7f26b1 | ||
|
|
51fd8f29fb | ||
|
|
732c373dfb | ||
|
|
cca7526b9b | ||
|
|
4b5b3195c4 | ||
|
|
3bbd1a8a48 | ||
|
|
8b1aeadaf0 | ||
|
|
ac2512799e | ||
|
|
8c8c0e78ca | ||
|
|
4018562bc7 | ||
|
|
23a00f9614 | ||
|
|
23dd68b212 | ||
|
|
8d248dab4c | ||
|
|
d5f60e1a47 | ||
|
|
263aa42725 | ||
|
|
c4393a7795 | ||
|
|
47cf874460 | ||
|
|
2945b3fe93 | ||
|
|
eae6f0427c | ||
|
|
dbf760c6e7 | ||
|
|
52840d68a8 | ||
|
|
821442ed8f | ||
|
|
048205ead5 | ||
|
|
7835da267b | ||
|
|
ceb9438000 | ||
|
|
401e13ca65 | ||
|
|
b1ad89fb4a | ||
|
|
6b727c43b9 | ||
|
|
74db837417 | ||
|
|
46c5df4e7b | ||
|
|
d3babd78aa | ||
|
|
f711336a6b | ||
|
|
5dd375dd20 | ||
|
|
8579909558 | ||
|
|
5c016f0993 | ||
|
|
a65f7b330b | ||
|
|
507d8b3fe4 | ||
|
|
0e5ea58294 | ||
|
|
f195b18f2d | ||
|
|
fb9eee47bd | ||
|
|
bd04cfaaa8 | ||
|
|
fe6e4686a2 | ||
|
|
ee20a10e94 | ||
|
|
cb9a05a7e7 | ||
|
|
b585063a26 | ||
|
|
2e2ca26020 | ||
|
|
654326a0d1 | ||
|
|
d757d8698d | ||
|
|
6d7b54eb62 | ||
|
|
e6223c9a49 | ||
|
|
d527769bf8 | ||
|
|
e9b80dae9c | ||
|
|
dee5186f65 | ||
|
|
c7686dee05 | ||
|
|
bff48e0b2d | ||
|
|
2df4625ce0 | ||
|
|
2c6c4a16ce | ||
|
|
deb5475b98 | ||
|
|
c4ca781ce2 | ||
|
|
5141debae7 | ||
|
|
33bd9d4782 | ||
|
|
845025db8f | ||
|
|
e418cb8387 | ||
|
|
801a05891e | ||
|
|
06a65ecf71 | ||
|
|
ef48c2ed91 | ||
|
|
bebfc62721 | ||
|
|
6717aff8f5 | ||
|
|
e506fa1dbe | ||
|
|
a7f58e2b92 | ||
|
|
bdefdf5d4e | ||
|
|
6d5417709c | ||
|
|
831c614a60 | ||
|
|
af26d983e5 | ||
|
|
97cd74a373 | ||
|
|
d1e7254fbd | ||
|
|
1fa9648a21 | ||
|
|
d339d6b816 | ||
|
|
178d583bcd | ||
|
|
617e9dbb3a | ||
|
|
fe95b7670b | ||
|
|
eb5bb5fcc0 | ||
|
|
2ebb409999 | ||
|
|
03657df82a | ||
|
|
7182b59976 | ||
|
|
70418144cf | ||
|
|
acaa79cf6b | ||
|
|
b313cbce0d | ||
|
|
a49a3103e2 | ||
|
|
ede6765f6b | ||
|
|
86dc1e81f1 | ||
|
|
e29cd8d83d | ||
|
|
21bfcd83f4 | ||
|
|
64f3a559ab | ||
|
|
83ac1f3555 | ||
|
|
ae3acbdbbc | ||
|
|
54882e061c | ||
|
|
2eb151c2d9 | ||
|
|
4d4da67247 | ||
|
|
dfa877262d | ||
|
|
6046be7fba | ||
|
|
1e1a9f2cf4 | ||
|
|
b90d368ee7 | ||
|
|
659aeeb58e | ||
|
|
bab3da24e6 | ||
|
|
6396b8ce18 | ||
|
|
4a91bcde30 | ||
|
|
d04ac7656a | ||
|
|
c546182789 | ||
|
|
3942461c50 | ||
|
|
059d671af5 | ||
|
|
dc6a1936f5 | ||
|
|
8bbcf0ab36 | ||
|
|
dcc74af87b | ||
|
|
3f83780049 | ||
|
|
1fe0d51925 | ||
|
|
07aa74eaf5 | ||
|
|
4eefc52748 | ||
|
|
822e62c238 | ||
|
|
da67963094 | ||
|
|
de06e63334 | ||
|
|
68a6c496d9 | ||
|
|
374bf79911 | ||
|
|
dad6031bd6 | ||
|
|
c93aa3810d | ||
|
|
6ad4a623c4 | ||
|
|
2bfe640a08 | ||
|
|
b095d1f33a | ||
|
|
f06c3578f6 | ||
|
|
7aa7ea0fe2 | ||
|
|
ea5dfe23a0 | ||
|
|
4bfb31b03b | ||
|
|
ed3cb7df41 | ||
|
|
6d3d2cd458 | ||
|
|
33e56272ca | ||
|
|
ecd95141f3 | ||
|
|
55b1e7b186 | ||
|
|
2dfa622357 | ||
|
|
907c5fe198 | ||
|
|
c02ac2ac66 | ||
|
|
d56f1d847c | ||
|
|
ec739b3392 | ||
|
|
1a6fe946ce | ||
|
|
08e874a026 | ||
|
|
d151bdb4cd | ||
|
|
815c4b4c45 | ||
|
|
acd3568f06 | ||
|
|
8646e9e404 | ||
|
|
fc78202600 | ||
|
|
fae185ff66 | ||
|
|
e793413d76 | ||
|
|
c2fb0394bf | ||
|
|
ca7fe9a520 | ||
|
|
e5bedf088e | ||
|
|
e2cbaaef71 | ||
|
|
1291841d98 | ||
|
|
592c70e655 | ||
|
|
9de6c08d28 | ||
|
|
2bb01e2d1a | ||
|
|
76939e4f63 | ||
|
|
b211eb199c | ||
|
|
66b101fe4c | ||
|
|
0aa763b706 | ||
|
|
5c7dcaba81 | ||
|
|
070a9c077e | ||
|
|
f6b879233c | ||
|
|
2d5e55964a | ||
|
|
1977a5fcda | ||
|
|
4e21cdb64b | ||
|
|
1e427890de | ||
|
|
7c09a8f827 | ||
|
|
ffb00b502a | ||
|
|
f2d2075d81 | ||
|
|
2a30a86e7d | ||
|
|
ba3ab7b322 | ||
|
|
501120701e | ||
|
|
2d65312910 | ||
|
|
c98b64fd82 | ||
|
|
7389744ecf | ||
|
|
301798d87a | ||
|
|
ed5c74affe | ||
|
|
f0066d6006 | ||
|
|
a9fa093d4b | ||
|
|
7f55412059 | ||
|
|
e413186d23 | ||
|
|
96412b3219 | ||
|
|
1b49bb8b22 | ||
|
|
a87ee6d059 | ||
|
|
c65158312d | ||
|
|
15cb9fc35b | ||
|
|
3f4781cdcb | ||
|
|
27bc5c803f | ||
|
|
d4e77deda4 | ||
|
|
cfb4b11573 | ||
|
|
6b6d3369b3 | ||
|
|
92b1b1365e | ||
|
|
6fabf4764b | ||
|
|
7a4945ff9e | ||
|
|
9af29364c7 | ||
|
|
94aa5487fb | ||
|
|
b98fcaa829 | ||
|
|
25282f3094 | ||
|
|
0ab637c7ab | ||
|
|
33e75d24f8 | ||
|
|
137bae1b04 | ||
|
|
d26337a1ba | ||
|
|
67fd119de8 | ||
|
|
b9f6fb9545 | ||
|
|
b9736cbd74 | ||
|
|
5cfa22cf23 | ||
|
|
a45b87d9b7 | ||
|
|
281b67952a | ||
|
|
f0202228d0 | ||
|
|
bf5a2252af | ||
|
|
9829d74a04 | ||
|
|
c5616c7869 | ||
|
|
bd917f2348 | ||
|
|
34518d328e | ||
|
|
d2ba57c1ab | ||
|
|
a21dad4c45 | ||
|
|
1ee555e468 | ||
|
|
0056a94115 | ||
|
|
900ccebb50 | ||
|
|
5c140b3f58 | ||
|
|
e65b605912 | ||
|
|
07580b8444 | ||
|
|
5d45dee686 | ||
|
|
827aa72e99 | ||
|
|
2a87585665 | ||
|
|
d70c3003d3 | ||
|
|
8376d33d77 | ||
|
|
6938fbafb5 | ||
|
|
fdc9eed514 | ||
|
|
2b4a8ed89c | ||
|
|
4b846ae4e0 | ||
|
|
cb97722fc3 | ||
|
|
57d866f7b7 | ||
|
|
467825c2d1 | ||
|
|
794b70daea | ||
|
|
903389d7b7 | ||
|
|
a9ce9c9717 | ||
|
|
3a5fda44e4 | ||
|
|
1208206bc0 | ||
|
|
16a15bf934 | ||
|
|
0243d886c3 | ||
|
|
f9a3878220 | ||
|
|
4a46f7b1eb | ||
|
|
1a7c9d5ca1 | ||
|
|
f1acad85db | ||
|
|
3f4f8b9e77 | ||
|
|
659eb1c1e8 | ||
|
|
af43ccfc07 | ||
|
|
ec5bc23dfd | ||
|
|
3743c6e86e | ||
|
|
c435d48885 | ||
|
|
49cd578816 | ||
|
|
8bd8ae001b | ||
|
|
59da69087c | ||
|
|
fd3ddbeafb | ||
|
|
dea850b503 | ||
|
|
ef4b8e8c1c | ||
|
|
f0d287588a | ||
|
|
49976b1934 | ||
|
|
ee4a847784 | ||
|
|
73c6f1ca3b | ||
|
|
aa400cbc12 | ||
|
|
3bcbc50525 | ||
|
|
c9b717ed68 | ||
|
|
01bfa88309 | ||
|
|
2fddf750e6 | ||
|
|
678b891ff7 | ||
|
|
b149fe371e | ||
|
|
88737248ab | ||
|
|
63c093248d | ||
|
|
f89aec867a | ||
|
|
8d8e7dab3c | ||
|
|
0d05385585 | ||
|
|
495a3cedb0 | ||
|
|
b134ae2cb4 | ||
|
|
ae80976689 | ||
|
|
7e9f82bc17 | ||
|
|
beda7ebbf2 | ||
|
|
9803468a77 | ||
|
|
cd3484d936 | ||
|
|
b82e001478 | ||
|
|
bf06d82870 | ||
|
|
e6aa3e1494 | ||
|
|
4c2004d479 | ||
|
|
d21cbb8b1f | ||
|
|
4b74a5ff35 | ||
|
|
0bc9997a95 | ||
|
|
9e98ded06c | ||
|
|
3f150f3f4e | ||
|
|
a4d86d24e6 | ||
|
|
12fa023c6d | ||
|
|
54760ef3bb | ||
|
|
1b23a7912a | ||
|
|
456096092d | ||
|
|
6e24cb038a | ||
|
|
1f26953504 | ||
|
|
10caae4dc6 | ||
|
|
6abc4c239a | ||
|
|
8aabee352c | ||
|
|
2c9af4784e | ||
|
|
dba64e77f1 | ||
|
|
45d423c0ce | ||
|
|
a45bb905f5 | ||
|
|
3216eca4a8 | ||
|
|
2f097cd016 | ||
|
|
e9da157cc7 | ||
|
|
04ce72a6ae | ||
|
|
eb18320950 | ||
|
|
17de7db70b | ||
|
|
a3226b1eb5 | ||
|
|
655d5888a6 | ||
|
|
1db81da020 | ||
|
|
4817b37742 | ||
|
|
be42b67f79 | ||
|
|
d7d4bf475b | ||
|
|
aa554eb767 | ||
|
|
001e0ce1cf | ||
|
|
1cb2a62fa8 | ||
|
|
f0e78309cb | ||
|
|
07b06348ab | ||
|
|
2ad14b379e | ||
|
|
3bf17d9df4 | ||
|
|
0e7efc0cf8 | ||
|
|
aea0d93390 | ||
|
|
dd318e679d | ||
|
|
1f903ba231 | ||
|
|
4f9628ed43 | ||
|
|
05787fdba9 | ||
|
|
94cbd70370 | ||
|
|
9452e0699e | ||
|
|
3ed00d3be7 | ||
|
|
cbf2de6c27 | ||
|
|
29217946c3 | ||
|
|
13f4432bbd | ||
|
|
d0e9c82fea | ||
|
|
63d42745ef | ||
|
|
a80f760933 | ||
|
|
94b7fc483a | ||
|
|
c32bf7780e | ||
|
|
155348d712 | ||
|
|
95f763463f | ||
|
|
93df5e3e54 | ||
|
|
4acecfa59a | ||
|
|
6790bfd0c0 | ||
|
|
1ce1f216ca | ||
|
|
38c8a4fb60 | ||
|
|
928c0d3de8 | ||
|
|
f718e1f49c | ||
|
|
a449be784e | ||
|
|
59834fba31 | ||
|
|
3eb92f7ea3 | ||
|
|
139a10e8f9 | ||
|
|
6283ef7f5e | ||
|
|
04ccc84bf6 | ||
|
|
f6ffaaff98 | ||
|
|
bf1f4aefa6 | ||
|
|
618d9bc7a9 | ||
|
|
70832286bc | ||
|
|
929a43c9fa | ||
|
|
649b064cf3 | ||
|
|
8e127cf4fa | ||
|
|
7deb570b61 | ||
|
|
9f6041b2a7 | ||
|
|
8d6add13c0 | ||
|
|
78c25cef21 | ||
|
|
a3007b7335 | ||
|
|
d663d553c5 | ||
|
|
c35dbc7211 | ||
|
|
0d566a6f3a | ||
|
|
9bbc05051c | ||
|
|
019c1302cb | ||
|
|
cbee76eac8 | ||
|
|
62716c4193 | ||
|
|
7dd9efd811 |
No files matched your search
@@ -12,6 +12,7 @@
|
||||
.idea
|
||||
.vscode
|
||||
/dist
|
||||
/docker-build
|
||||
/mac-build
|
||||
/pip-build
|
||||
/src-build
|
||||
@@ -37,4 +38,4 @@ runtime/ui_BrowserWindow.h
|
||||
web/config_local.py
|
||||
web/regression/test_config.json
|
||||
node_modules/
|
||||
yarn.lock
|
||||
web/pgAdmin/static/js/generated
|
||||
@@ -164,6 +164,12 @@ REM Main function Ends
|
||||
CD "%WD%\web"
|
||||
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
|
||||
ECHO Install Javascript dependencies
|
||||
call yarn install
|
||||
ECHO Bundle all Javascript
|
||||
call yarn run bundle
|
||||
|
||||
XCOPY /S /I /E /H /Y "%WD%\web" "%PGBUILDPATH%\web" > nul
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
|
||||
@@ -201,6 +207,9 @@ REM Main function Ends
|
||||
ECHO Removing Sphinx
|
||||
pip uninstall -y sphinx Pygments alabaster colorama docutils imagesize requests snowballstemmer
|
||||
|
||||
ECHO Fixing backports.csv - adding missing __init__.py
|
||||
type nul >> "%PGBUILDPATH%\%VIRTUALENV%\Lib\site-packages\backports\__init__.py"
|
||||
|
||||
ECHO Assembling runtime environment...
|
||||
CD "%WD%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
@@ -247,10 +256,19 @@ REM Main function Ends
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\Qt5MultimediaWidgets.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\libQt5WebKit.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\libQt5WebKitWidgets.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
|
||||
IF %QT_VERSION% GEQ 5.9 (
|
||||
copy "%QTDIR%\bin\Qt5WebKit.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\Qt5WebKitWidgets.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
) ELSE (
|
||||
copy "%QTDIR%\bin\libQt5WebKit.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\libQt5WebKitWidgets.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
)
|
||||
|
||||
copy "%QTDIR%\bin\icudt57.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\icuin57.dll" "%PGBUILDPATH%\runtime"
|
||||
@@ -259,13 +277,13 @@ REM Main function Ends
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\libgcc_s_dw2-1.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\libstdc++-6.dll" "%PGBUILDPATH%\runtime"
|
||||
copy "%QTDIR%\bin\libstdc++-6.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\libwinpthread-1.dll" "%PGBUILDPATH%\runtime"
|
||||
copy "%QTDIR%\bin\libwinpthread-1.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\libxml2-2.dll" "%PGBUILDPATH%\runtime"
|
||||
copy "%QTDIR%\bin\libxml2-2.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
copy "%QTDIR%\bin\libxslt-1.dll" "%PGBUILDPATH%\runtime"
|
||||
copy "%QTDIR%\bin\libxslt-1.dll" "%PGBUILDPATH%\runtime"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
MKDIR "%PGBUILDPATH%\runtime\platforms"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
@@ -384,7 +402,6 @@ REM Main function Ends
|
||||
:CLEANUP_ENV
|
||||
ECHO Cleaning up private environment...
|
||||
rd "%PGBUILDPATH%\%VIRTUALENV%\Include" /S /Q
|
||||
rd "%PGBUILDPATH%\%VIRTUALENV%\Scripts" /S /Q
|
||||
DEL /s "%PGBUILDPATH%\%VIRTUALENV%\pip-selfcheck.json"
|
||||
|
||||
ECHO Cleaned up private environment successfully.
|
||||
|
||||
@@ -238,6 +238,12 @@ REM Main function Ends
|
||||
CD "%WD%\web"
|
||||
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
|
||||
ECHO Install Javascript dependencies
|
||||
call yarn install
|
||||
ECHO Bundle all Javascript
|
||||
call yarn run bundle
|
||||
|
||||
XCOPY /S /I /E /H /Y "%WD%\web" "%PGBUILDPATH%\web" > nul
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
|
||||
@@ -500,7 +506,6 @@ REM Main function Ends
|
||||
:CLEANUP_ENV
|
||||
ECHO Cleaning up private environment...
|
||||
rd "%PGBUILDPATH%\%VIRTUALENV%\Include" /S /Q
|
||||
rd "%PGBUILDPATH%\%VIRTUALENV%\Scripts" /S /Q
|
||||
DEL /s "%PGBUILDPATH%\%VIRTUALENV%\pip-selfcheck.json"
|
||||
|
||||
ECHO Cleaned up private environment successfully.
|
||||
|
||||
@@ -16,23 +16,65 @@ SHELL = /bin/sh
|
||||
# Include only platform-independent builds in all
|
||||
all: docs pip src
|
||||
|
||||
# Include all clean sub-targets in clean
|
||||
clean: clean-dist clean-docs clean-pip clean-appbundle clean-src
|
||||
|
||||
pip: docs
|
||||
./pkg/pip/build.sh
|
||||
|
||||
appbundle: docs
|
||||
./pkg/mac/build.sh
|
||||
|
||||
appbundle-webkit: docs
|
||||
PGADMIN4_USE_WEBKIT=1 ./pkg/mac/build.sh
|
||||
|
||||
src:
|
||||
./pkg/src/build.sh
|
||||
install-node:
|
||||
cd web && yarn install
|
||||
|
||||
minimise:
|
||||
python web/tools/minimise.py ./web
|
||||
bundle:
|
||||
cd web && yarn run bundle
|
||||
|
||||
bundle-dev:
|
||||
cd web && yarn run bundle:dev
|
||||
|
||||
linter:
|
||||
cd web && yarn run linter
|
||||
|
||||
check: install-node bundle linter
|
||||
cd web && yarn run karma start -- --single-run && python regression/runtests.py
|
||||
|
||||
check-python: install-node bundle
|
||||
cd web && python regression/runtests.py --exclude feature_tests
|
||||
|
||||
check-feature: install-node bundle
|
||||
cd web && python regression/runtests.py --pkg feature_tests
|
||||
|
||||
check-js: install-node linter
|
||||
cd web && yarn run karma start -- --single-run
|
||||
|
||||
# Include all clean sub-targets in clean
|
||||
clean: clean-appbundle clean-docker clean-dist clean-docs clean-pip clean-src
|
||||
|
||||
clean-appbundle:
|
||||
rm -rf mac-build/
|
||||
|
||||
clean-docker:
|
||||
rm -rf docker-build/
|
||||
|
||||
clean-dist:
|
||||
rm -rf dist/
|
||||
|
||||
clean-docs:
|
||||
LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 $(MAKE) -C docs/en_US -f Makefile.sphinx clean
|
||||
|
||||
clean-pip:
|
||||
rm -rf pip-build/
|
||||
|
||||
clean-src:
|
||||
rm -rf src-build/
|
||||
|
||||
docker:
|
||||
./pkg/docker/build.sh
|
||||
|
||||
docs:
|
||||
LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 $(MAKE) -C docs/en_US -f Makefile.sphinx html
|
||||
|
||||
msg-compile:
|
||||
cd web && pybabel compile -d pgadmin/translations
|
||||
|
||||
msg-extract:
|
||||
cd web && pybabel extract -F babel.cfg -o pgadmin/messages.pot pgadmin
|
||||
@@ -40,28 +82,10 @@ msg-extract:
|
||||
msg-update:
|
||||
cd web && pybabel update -i pgadmin/messages.pot -d pgadmin/translations
|
||||
|
||||
msg-compile:
|
||||
cd web && pybabel compile -d pgadmin/translations
|
||||
|
||||
docs:
|
||||
LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 $(MAKE) -C docs/en_US -f Makefile.sphinx html
|
||||
|
||||
clean-pip:
|
||||
rm -rf pip-build/
|
||||
|
||||
clean-appbundle:
|
||||
rm -rf mac-build/
|
||||
|
||||
clean-src:
|
||||
rm -rf src-build/
|
||||
|
||||
clean-docs:
|
||||
LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 $(MAKE) -C docs/en_US -f Makefile.sphinx clean
|
||||
|
||||
clean-dist:
|
||||
rm -rf dist/
|
||||
|
||||
check:
|
||||
python web/regression/runtests.py
|
||||
|
||||
.PHONY: docs
|
||||
|
||||
pip: docs
|
||||
./pkg/pip/build.sh
|
||||
|
||||
src:
|
||||
./pkg/src/build.sh
|
||||
@@ -18,7 +18,7 @@ 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 browser and Python interpretor in one package which is
|
||||
it is essentially a browser and Python interpreter in one package which is
|
||||
capable of hosting the Python application and presenting it to the user as a
|
||||
desktop application.
|
||||
|
||||
@@ -69,6 +69,22 @@ 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.
|
||||
|
||||
Create Database Migrations
|
||||
--------------------------
|
||||
|
||||
In order to make changes to the SQLite DB, navigate to the 'web' directory:
|
||||
|
||||
(pgadmin4) $ cd $PGADMIN4_SRC/web
|
||||
|
||||
Create a migration file with the following command:
|
||||
|
||||
(pgadmin4) $ FLASK_APP=pgAdmin4.py flask db revision
|
||||
|
||||
This will create a file in: $PGADMIN4_SRC/web/migrations/versions/ .
|
||||
Add any changes to the 'upgrade' function.
|
||||
|
||||
There is no need to increment the SETTINGS_SCHEMA_VERSION.
|
||||
|
||||
Configuring the Python Environment
|
||||
----------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "EXECUTING: Feature tests"
|
||||
echo
|
||||
|
||||
echo "Starting virtual frame buffer..."
|
||||
echo
|
||||
|
||||
Xvfb -ac :99 -screen 0 1280x1024x16 &
|
||||
FB_PID=$!
|
||||
export DISPLAY=:99
|
||||
|
||||
echo "Creating Python ${PYTHON_VERSION} virtual environment..."
|
||||
echo
|
||||
|
||||
cd $WORKSPACE/
|
||||
|
||||
PYTHON_SUFFIX=""
|
||||
|
||||
if [[ "$PYTHON_VERSION" == 3* ]]; then
|
||||
PYTHON_SUFFIX="3"
|
||||
fi
|
||||
|
||||
if [ "$PYTHON_VERSION" == 2.6 -o "$PYTHON_VERSION" == 3.3 ]; then
|
||||
/usr/bin/virtualenv --no-wheel -p /usr/local/python-$PYTHON_VERSION/bin/python$PYTHON_SUFFIX $WORKSPACE/pgadmin-venv || { echo 'ERROR: Failed to create the Python virtual environment.' ; exit 1; }
|
||||
. $WORKSPACE/pgadmin-venv/bin/activate || { echo 'ERROR: Failed to activate the Python virtual environment.' ; exit 1; }
|
||||
$WORKSPACE/pgadmin-venv/bin/pip install wheel==0.29.0 || { echo 'ERROR: Failed to install wheel 0.29.0.' ; exit 1; }
|
||||
else
|
||||
/usr/bin/virtualenv -p /usr/local/python-$PYTHON_VERSION/bin/python$PYTHON_SUFFIX $WORKSPACE/pgadmin-venv || { echo 'ERROR: Failed to create the Python virtual environment.' ; exit 1; }
|
||||
. $WORKSPACE/pgadmin-venv/bin/activate || { echo 'ERROR: Failed to activate the Python virtual environment.' ; exit 1; }
|
||||
fi
|
||||
|
||||
$WORKSPACE/pgadmin-venv/bin/pip install -r requirements.txt || { echo 'ERROR: Failed to install the application requirements.' ; exit 1; }
|
||||
$WORKSPACE/pgadmin-venv/bin/pip install -r web/regression/requirements.txt || { echo 'ERROR: Failed to install the regression test requirements.' ; exit 1; }
|
||||
|
||||
cd web
|
||||
/usr/bin/yarn install || { echo 'ERROR: Failed to install the required Javascript modules.' ; exit 1; }
|
||||
/usr/bin/yarn run bundle || { echo 'ERROR: Failed to bundle the Javascript code.' ; exit 1; }
|
||||
cd ../
|
||||
|
||||
echo "Running regression tests..."
|
||||
echo
|
||||
|
||||
$WORKSPACE/pgadmin-venv/bin/python $WORKSPACE/web/regression/runtests.py --pkg feature_tests || { echo 'ERROR: Error detected when running the Feature tests.' ; exit 1; }
|
||||
|
||||
echo "Cleaning up..."
|
||||
echo
|
||||
|
||||
kill $FB_PID
|
||||
@@ -5,5 +5,6 @@ echo
|
||||
|
||||
cd $WORKSPACE/web/
|
||||
|
||||
/bin/npm install || { echo 'ERROR: Failed to install the required Javascript modules.' ; exit 1; }
|
||||
/usr/bin/yarn install || { echo 'ERROR: Failed to install the required Javascript modules.' ; exit 1; }
|
||||
/usr/bin/yarn run linter || { echo 'ERROR: Failed to lint the Javascript code.' ; exit 1; }
|
||||
./node_modules/.bin/karma start --single-run || { echo 'ERROR: Error detected when running the Jasmine tests.' ; exit 1; }
|
||||
@@ -3,13 +3,6 @@
|
||||
echo "EXECUTING: Python tests"
|
||||
echo
|
||||
|
||||
echo "Starting virtual frame buffer..."
|
||||
echo
|
||||
|
||||
Xvfb -ac :99 -screen 0 1280x1024x16 &
|
||||
FB_PID=$!
|
||||
export DISPLAY=:99
|
||||
|
||||
echo "Creating Python ${PYTHON_VERSION} virtual environment..."
|
||||
echo
|
||||
|
||||
@@ -21,8 +14,15 @@ if [[ "$PYTHON_VERSION" == 3* ]]; then
|
||||
PYTHON_SUFFIX="3"
|
||||
fi
|
||||
|
||||
/usr/bin/virtualenv -p /usr/local/python-$PYTHON_VERSION/bin/python$PYTHON_SUFFIX $WORKSPACE/pgadmin-venv || { echo 'ERROR: Failed to create the Python virtual environment.' ; exit 1; }
|
||||
. $WORKSPACE/pgadmin-venv/bin/activate || { echo 'ERROR: Failed to activate the Python virtual environment.' ; exit 1; }
|
||||
if [ "$PYTHON_VERSION" == 2.6 -o "$PYTHON_VERSION" == 3.3 ]; then
|
||||
/usr/bin/virtualenv --no-wheel -p /usr/local/python-$PYTHON_VERSION/bin/python$PYTHON_SUFFIX $WORKSPACE/pgadmin-venv || { echo 'ERROR: Failed to create the Python virtual environment.' ; exit 1; }
|
||||
. $WORKSPACE/pgadmin-venv/bin/activate || { echo 'ERROR: Failed to activate the Python virtual environment.' ; exit 1; }
|
||||
$WORKSPACE/pgadmin-venv/bin/pip install wheel==0.29.0 || { echo 'ERROR: Failed to install wheel 0.29.0.' ; exit 1; }
|
||||
else
|
||||
/usr/bin/virtualenv -p /usr/local/python-$PYTHON_VERSION/bin/python$PYTHON_SUFFIX $WORKSPACE/pgadmin-venv || { echo 'ERROR: Failed to create the Python virtual environment.' ; exit 1; }
|
||||
. $WORKSPACE/pgadmin-venv/bin/activate || { echo 'ERROR: Failed to activate the Python virtual environment.' ; exit 1; }
|
||||
fi
|
||||
|
||||
$WORKSPACE/pgadmin-venv/bin/pip install -r requirements.txt || { echo 'ERROR: Failed to install the application requirements.' ; exit 1; }
|
||||
$WORKSPACE/pgadmin-venv/bin/pip install -r web/regression/requirements.txt || { echo 'ERROR: Failed to install the regression test requirements.' ; exit 1; }
|
||||
|
||||
@@ -31,7 +31,3 @@ echo
|
||||
|
||||
$WORKSPACE/pgadmin-venv/bin/python $WORKSPACE/web/regression/runtests.py --exclude feature_tests || { echo 'ERROR: Error detected when running the Python tests.' ; exit 1; }
|
||||
|
||||
echo "Cleaning up..."
|
||||
echo
|
||||
|
||||
kill $FB_PID
|
||||
@@ -2,6 +2,14 @@ import os
|
||||
import sys
|
||||
import inspect
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
import builtins
|
||||
else:
|
||||
import __builtin__ as builtins
|
||||
|
||||
# Ensure the global server mode is set.
|
||||
builtins.SERVER_MODE = None
|
||||
|
||||
root = os.path.realpath(
|
||||
os.path.dirname(
|
||||
os.path.realpath(__file__)
|
||||
|
||||
@@ -13,6 +13,14 @@
|
||||
|
||||
import sys, os
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
import builtins
|
||||
else:
|
||||
import __builtin__ as builtins
|
||||
|
||||
# Ensure the global server mode is set.
|
||||
builtins.SERVER_MODE = None
|
||||
|
||||
# Import the pgAdmin config
|
||||
root = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, root + '/../../web/')
|
||||
|
||||
@@ -16,12 +16,13 @@ packages and users interested in understanding how pgAdmin works.**
|
||||
Configuration
|
||||
*************
|
||||
|
||||
In order to configure pgAdmin to run in desktop mode, it is first necessary to
|
||||
configure the Python code to run in single-user mode, and then to configure the
|
||||
runtime to find and execute the code.
|
||||
From pgAdmin 4 v2 onwards, the default configuration mode is server, however,
|
||||
this is overridden by the desktop runtime at startup. In most environments, no
|
||||
Python configuration is required unless you wish to override other default
|
||||
settings.
|
||||
|
||||
Note that there are multiple configuration files that are read at startup by
|
||||
pgAdmin. These are as follows:
|
||||
There are multiple configuration files that are read at startup by pgAdmin. These
|
||||
are as follows:
|
||||
|
||||
* ``config.py``: This is the main configuration file, and should not be modified.
|
||||
It can be used as a reference for configuration settings, that may be overridden
|
||||
@@ -35,31 +36,11 @@ pgAdmin. These are as follows:
|
||||
for end users to change any default or packaging specific settings that they may
|
||||
wish to adjust to meet local preferences or standards.
|
||||
|
||||
Python
|
||||
------
|
||||
NOTE: If the SERVER_MODE setting is changed in ``config_distro.py`` or ``config_local.py``,
|
||||
you will most likely need to re-set the LOG_FILE, SQLITE_PATH, SESSION_DB_PATH
|
||||
and STORAGE_DIR values as well as they will have been set based on the default
|
||||
configuration or overridden by the runtime.
|
||||
|
||||
In order to configure the Python code, follow these steps:
|
||||
|
||||
1. Ensure that any existing configuration database (``pgadmin4.db``) is moved
|
||||
out of the way in the ``web/`` directory containing the pgAdmin Python code.
|
||||
|
||||
2. Create a ``config_local.py`` file alongside the existing ``config.py`` file.
|
||||
|
||||
3. Edit ``config_local.py`` and add the following setting:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SERVER_MODE = False
|
||||
|
||||
4. Run the following command to create the configuration database:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ python setup.py
|
||||
|
||||
Alternatively, you can simply run ``pgAdmin4.py`` at this point or aat a later time,
|
||||
and ``pgadmin4.db`` will be created automatically at first run.
|
||||
|
||||
Runtime
|
||||
-------
|
||||
|
||||
@@ -73,6 +54,25 @@ semi-colon character, for example:
|
||||
|
||||
/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/;/Users/dpage/python-libs/
|
||||
|
||||
The configuration settings are stored using the QSettings class in Qt, which
|
||||
will use an INI file on Unix systems, a plist file on Mac OS X, and the registry
|
||||
on Windows. The Python Path setting is stored in the ``PythonPath`` key.
|
||||
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).
|
||||
|
||||
The configuration settings:
|
||||
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Key | Type | Purpose |
|
||||
+==========================+====================+===============================================================+
|
||||
| ApplicationPath | String | The directory containing pgAdmin4.py |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| ConnectionTimeout | Integer | The number of seconds to wait for application server startup. |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| PythonPath | String | The Python module search path |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Browser/Geometry | Binary | The runtime window's size/shape |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Browser/WindowState | Binary | The runtime window's state |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Browser/Zoom | String | The runtime window's zoom level (zoom % / 100) |
|
||||
+--------------------------+--------------------+---------------------------------------------------------------+
|
||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 227 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 39 KiB |
@@ -24,6 +24,7 @@ Contents:
|
||||
contributions
|
||||
pgagent
|
||||
licence
|
||||
release_notes
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ When running in the Desktop Runtime, the following keyboard shortcuts are availa
|
||||
+--------------------------+----------------+---------------------------------------+
|
||||
| Ctrl+Minus | Cmd+Minus | Zoom out |
|
||||
+--------------------------+----------------+---------------------------------------+
|
||||
| Ctrl+0 | Cmd+0 | Reset the zoom level |
|
||||
+--------------------------+----------------+---------------------------------------+
|
||||
|
||||
**SQL Editors**
|
||||
|
||||
@@ -39,6 +41,12 @@ When using the syntax-highlighting SQL editors, the following shortcuts are avai
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
| Ctrl+Alt+Right | Cmd+Option+Right | Move right one word |
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
| Ctrl+/ | Cmd+/ | Comment selected code (Inline) |
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
| Ctrl+. | Cmd+. | Uncomment selected code (Inline) |
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
| Ctrl+Shift+/ | Cmd+Shift+/ | Comment/Uncomment code (Block) |
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
| Ctrl+A | Cmd+A | Select all |
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
| Ctrl+C | Cmd+C | Copy selected text to the clipboard |
|
||||
@@ -53,6 +61,10 @@ When using the syntax-highlighting SQL editors, the following shortcuts are avai
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
| Ctrl+Minus | Cmd+Minus | Zoom out |
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
| Tab | Tab | Indent selected text |
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
| Shift+Tab | Shift+Tab | Un-indent selected text |
|
||||
+--------------------------+------------------+-------------------------------------+
|
||||
|
||||
**Query Tool**
|
||||
|
||||
@@ -77,9 +89,7 @@ When using the Query Tool, the following shortcuts are available:
|
||||
+--------------------------+--------------------+-----------------------------------+
|
||||
| Ctrl+G | Cmd+G | Find next |
|
||||
+--------------------------+--------------------+-----------------------------------+
|
||||
| Shift+Ctrl+G | Shift+Cmd+G | Find previous |
|
||||
| Ctrl+Shift+G | Cmd+Shift+G | Find previous |
|
||||
+--------------------------+--------------------+-----------------------------------+
|
||||
| Shift+Ctrl+F | Shift+Cmd+F | Replace |
|
||||
| Ctrl+Shift+F | Cmd+Shift+F | Replace |
|
||||
+--------------------------+--------------------+-----------------------------------+
|
||||
| Shift+Ctrl+R | Shift+Cmd+Option+F | Replace all |
|
||||
+--------------------------+--------------------+-----------------------------------+
|
||||
@@ -4,23 +4,23 @@
|
||||
pgAdmin Preferences
|
||||
*******************
|
||||
|
||||
pgAdmin 4 has a selection of configuration options (*Preferences*) that you can use to customize your pgAdmin client. To open the *Preferences* dialog, select *Preferences* from the *File* menu.
|
||||
pgAdmin 4 has a selection of configuration options (*Preferences*) that you can use to customize your pgAdmin client. To open the *Preferences* dialog, select *Preferences* from the *File* menu.
|
||||
|
||||
.. image:: images/preferences_tree.png
|
||||
|
||||
The left pane of the *Preferences* dialog displays a tree control; each node of the tree control provides access to options that are related to the selected node.
|
||||
The left pane of the *Preferences* dialog displays a tree control; each node of the tree control provides access to options that are related to the selected node.
|
||||
|
||||
* Use the plus sign (+) to the left of a node to expand a segment of the tree control.
|
||||
* Click the minus sign (-) to the left of a node to close that node.
|
||||
* Use the plus sign (+) to the left of a node to expand a segment of the tree control.
|
||||
* Click the minus sign (-) to the left of a node to close that node.
|
||||
|
||||
Expand the **Browser** node of the tree control to personalize your workspace.
|
||||
Expand the **Browser** node of the tree control to personalize your workspace.
|
||||
|
||||
.. image:: images/preferences_browser_display.png
|
||||
|
||||
Use the options on the *Display* dialog to specify general display preferences:
|
||||
|
||||
Move the *Show system objects* switch to the *True* position to display system objects in the *pgAdmin* tree control. This option instructs pgAdmin to display objects such as system schemas (e.g. pg_temp*) and system columns (e.g. xmin, ctid) in the tree control.
|
||||
|
||||
|
||||
Use the options on the *Nodes* dialog to select the object types that will be displayed in the *pgAdmin* tree control.
|
||||
|
||||
.. image:: images/preferences_browser_nodes.png
|
||||
@@ -33,7 +33,7 @@ Expand the **Dashboards** node to specify your graphing preferences.
|
||||
|
||||
Use the options on the *Graphs* dialog specify a refresh rate for statistics, transaction throughput and tuples. The rate you specify will affect a corresponding graph on the *Dashboard* tab of the *pgAdmin* tabbed browser.
|
||||
|
||||
Expand the **Paths** node to specify the locations of supporting files.
|
||||
Expand the **Paths** node to specify the locations of supporting files.
|
||||
|
||||
.. image:: images/preferences_paths_binary.png
|
||||
|
||||
@@ -43,7 +43,7 @@ Use the fields in the *Binary paths* node to specify paths to the PostgreSQL bin
|
||||
|
||||
Use the *Help* dialog to customize links to support documentation.
|
||||
|
||||
* Use the *EDB Advanced Server Help Path* to find a link path for EnterpriseDB Postgres Advanced Server documentation on the company website. This link is editable: substitute the applicable PostgreSQL version number for *$VERSION$*, or provide an alternate link path.
|
||||
* Use the *EDB Advanced Server Help Path* to find a link path for EnterpriseDB Postgres Advanced Server documentation on the company website. This link is editable: substitute the applicable PostgreSQL version number for *$VERSION$*, or provide an alternate link path.
|
||||
* Use the *PostgreSQL Help Path* to find a link path to the current set of PostgreSQL core documentation. This link is editable: substitute the applicable PostgreSQL version number for *$VERSION$*, or provide an alternate link path.
|
||||
|
||||
Expand the **SQL Editor** node to specify your preferences for the SQL Editor tool.
|
||||
@@ -68,7 +68,13 @@ Use the options on the *Explain Options* dialog to specify the level of detail i
|
||||
Use the options in the *Options* dialog to manage modifications to a SQL statement.
|
||||
|
||||
* Move the *Auto-Commit* switch to the *True* position to commit a SQL statement upon completion.
|
||||
* Move the *Auto-Rollback* switch to the *True* to rollback a SQL statement to the beginning of the statement or to a prior rollback.
|
||||
* Move the *Auto-Rollback* switch to the *True* to rollback a SQL statement to the beginning of the statement or to a prior rollback.
|
||||
* Move the *Brace matching* switch to the *True* to highlight the matched braces in editor.
|
||||
* Move the *Font size* field to specify the font size.
|
||||
* Move the *Insert bracket pairs* switch to the *True* to insert pair brackets in the editor.
|
||||
* Move the *Line wrapping* switch to the *True* to wrap the long lines in the editor.
|
||||
* Move the *Tab size* field to specify the number of spaces per tab in the editor.
|
||||
* Move the *Use spaces* switch to the *True* to insert spaces instead of tabs.
|
||||
|
||||
Expand the **Storage** node to specify a maximum file size for uploads.
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
*************
|
||||
Release Notes
|
||||
*************
|
||||
|
||||
|
||||
pgAdmin release notes provide information on the features and improvements in each release. This page includes release
|
||||
notes for major releases and minor (bugfix) releases. Select your version from the list below to see the release notes
|
||||
for it.
|
||||
|
||||
.. toctree::
|
||||
|
||||
release_notes_2_0
|
||||
release_notes_1_6
|
||||
release_notes_1_5
|
||||
release_notes_1_4
|
||||
release_notes_1_3
|
||||
release_notes_1_2
|
||||
release_notes_1_1
|
||||
release_notes_1_0
|
||||
@@ -0,0 +1,20 @@
|
||||
***********
|
||||
Version 1.0
|
||||
***********
|
||||
|
||||
Release date: 2016-09-29
|
||||
|
||||
The first major release of pgAdmin 4. With a more modern look and feel, this release includes the following features;
|
||||
|
||||
* Multiplatform
|
||||
* Designed for multiple PostgreSQL versions and derivatives
|
||||
* Extensive documentation
|
||||
* Multiple deployment models
|
||||
* Tools
|
||||
* Routine maintenance
|
||||
* Create, view and edit all common PostgreSQL objects
|
||||
* Multibyte support
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
***********
|
||||
Version 1.1
|
||||
***********
|
||||
|
||||
Release date: 2016-10-27
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 1.0;
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #1328 <https://redmine.postgresql.org/issues/1328>`_ - Add Python 3.5 Support
|
||||
| `Feature #1859 <https://redmine.postgresql.org/issues/1859>`_ - Include wait information on the activity tab of the dashboards
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #1155 <https://redmine.postgresql.org/issues/1155>`_ - Display the start value when the user creates sequence
|
||||
| `Bug #1531 <https://redmine.postgresql.org/issues/1531>`_ - Fix to update privileges for Views and Materials Views where "string indices must be integers error" displayed
|
||||
| `Bug #1574 <https://redmine.postgresql.org/issues/1574>`_ - Display SQL in SQL pane for security label in PG and EPAS server
|
||||
| `Bug #1576 <https://redmine.postgresql.org/issues/1576>`_ - Make security label option available in procedure properties
|
||||
| `Bug #1577 <https://redmine.postgresql.org/issues/1577>`_ - Make debug option available for package function and procedure
|
||||
| `Bug #1596 <https://redmine.postgresql.org/issues/1596>`_ - Correct spelling error from evnt_turncate to evnt_truncate
|
||||
| `Bug #1599 <https://redmine.postgresql.org/issues/1599>`_ - Ensure the grant wizard works with objects with special characters in the name
|
||||
| `Bug #1622 <https://redmine.postgresql.org/issues/1622>`_ - Fix issue using special characters when creating synonym
|
||||
| `Bug #1728 <https://redmine.postgresql.org/issues/1728>`_ - Properties refreshing after objects are edited
|
||||
| `Bug #1739 <https://redmine.postgresql.org/issues/1739>`_ - Prevent the user from trying to.....
|
||||
| `Bug #1785 <https://redmine.postgresql.org/issues/1785>`_ - Correctly identify server type upon first connection
|
||||
| `Bug #1786 <https://redmine.postgresql.org/issues/1786>`_ - Ensure errorModel unset property is set correctly when adding a new server
|
||||
| `Bug #1808 <https://redmine.postgresql.org/issues/1808>`_ - Set seconds to valid value in pgAgent job schedule
|
||||
| `Bug #1817 <https://redmine.postgresql.org/issues/1817>`_ - Display message "server does not support ssl" if server with ca-cert or ca-full added
|
||||
| `Bug #1821 <https://redmine.postgresql.org/issues/1821>`_ - Optionally sign both the Mac app bundle and the disk image
|
||||
| `Bug #1822 <https://redmine.postgresql.org/issues/1822>`_ - Handle non-ascii responses from the server when connecting
|
||||
| `Bug #1823 <https://redmine.postgresql.org/issues/1823>`_ - Attempt to sign the Windows installer, failing with a warning if there's no cert available
|
||||
| `Bug #1824 <https://redmine.postgresql.org/issues/1824>`_ - Add documenation for pgAgent
|
||||
| `Bug #1835 <https://redmine.postgresql.org/issues/1835>`_ - Allow users to choose SELECT permissions for tables and sequences in the grant wizard
|
||||
| `Bug #1837 <https://redmine.postgresql.org/issues/1837>`_ - Fix refreshing of FTS dictionaries which was causing error "Connection to the server has been lost"
|
||||
| `Bug #1838 <https://redmine.postgresql.org/issues/1838>`_ - Don't append new objects with the wrong parent in tree browser if the correct one isn't loaded
|
||||
| `Bug #1843 <https://redmine.postgresql.org/issues/1843>`_ - Function definition matches value returned from pg_get_functiondef()
|
||||
| `Bug #1845 <https://redmine.postgresql.org/issues/1845>`_ - Allow refreshing synonym node. Does not display message "Unimplemented method (node) for this url (/browser/synonym/nodes/1/7/14301/2200/test)"
|
||||
| `Bug #1847 <https://redmine.postgresql.org/issues/1847>`_ - Identify the collation correctly when reverse engineering table SQL. ERROR: schema "default" does not exist no longer displayed
|
||||
| `Bug #1849 <https://redmine.postgresql.org/issues/1849>`_ - Remove security keys from config.py/config_local.py
|
||||
| `Bug #1857 <https://redmine.postgresql.org/issues/1857>`_ - Fix error while renaming FTS dictionary and FTS template nodes
|
||||
| `Bug #1858 <https://redmine.postgresql.org/issues/1858>`_ - Ensure the File Manager honours the file type while traversing the directories.
|
||||
| `Bug #1861 <https://redmine.postgresql.org/issues/1861>`_ - Properly generate exclusion constraint SQL.
|
||||
| `Bug #1863 <https://redmine.postgresql.org/issues/1863>`_ - Correctly quote type names in reverse engineered SQL for tables
|
||||
| `Bug #1864 <https://redmine.postgresql.org/issues/1864>`_ - Fix layout of DateTimePicker control help message.
|
||||
| `Bug #1867 <https://redmine.postgresql.org/issues/1867>`_ - Allow package bodies to be dropped.
|
||||
| `Bug #1868 <https://redmine.postgresql.org/issues/1868>`_ - Resolved issue where Integer type of preferences are not updated
|
||||
| `Bug #1872 <https://redmine.postgresql.org/issues/1872>`_ - Fix the file manager when used under Python 3.
|
||||
| `Bug #1877 <https://redmine.postgresql.org/issues/1877>`_ - Ensure preferences values are stored properly.
|
||||
| `Bug #1878 <https://redmine.postgresql.org/issues/1878>`_ - Ensure steps and schedules can be created in empty jobs. ProgrammingError: can't adapt type 'Undefined' was displayed
|
||||
| `Bug #1880 <https://redmine.postgresql.org/issues/1880>`_ - Add new indexes to the correct parent on the treeview.
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
***********
|
||||
Version 1.2
|
||||
***********
|
||||
|
||||
Release date: 2017-02-10
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 1.1.
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #1375 <https://redmine.postgresql.org/issues/1375>`_ - Migrate the runtime to QtWebEngine from QtWebKit
|
||||
| `Feature #1765 <https://redmine.postgresql.org/issues/1765>`_ - Find and replace functionality with regexp and group replacement
|
||||
| `Feature #1789 <https://redmine.postgresql.org/issues/1789>`_ - Column width of data output panel should fit to data (as pgAdmin III)
|
||||
| `Feature #1790 <https://redmine.postgresql.org/issues/1790>`_ - [Web] Support setting a field's value to "null"
|
||||
| `Feature #1848 <https://redmine.postgresql.org/issues/1848>`_ - macOS appbundle is missing postgresql binaries for import etc.
|
||||
| `Feature #1910 <https://redmine.postgresql.org/issues/1910>`_ - Remember last used directory in the file manager
|
||||
| `Feature #1911 <https://redmine.postgresql.org/issues/1911>`_ - Direct path navigation in the file manager
|
||||
| `Feature #1922 <https://redmine.postgresql.org/issues/1922>`_ - Improve handling of corrupt configuration databases
|
||||
| `Feature #1963 <https://redmine.postgresql.org/issues/1963>`_ - Add a Chinese (Simplified) translation
|
||||
| `Feature #1964 <https://redmine.postgresql.org/issues/1964>`_ - Create a docs tarball along with the source tarball
|
||||
| `Feature #2025 <https://redmine.postgresql.org/issues/2025>`_ - Allow the SQL Editors to word-wrap
|
||||
| `Feature #2124 <https://redmine.postgresql.org/issues/2124>`_ - Create a template loader to simplify SQL template location, and remove duplicate templates
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #1227 <https://redmine.postgresql.org/issues/1227>`_ - Display improved error message for Debugger listener starting error and reset between executions
|
||||
| `Bug #1267 <https://redmine.postgresql.org/issues/1267>`_ - Fix issue where MINIFY_HTML doesn't work with the docs
|
||||
| `Bug #1364 <https://redmine.postgresql.org/issues/1364>`_ - Ensure dialogue control buttons are consistent
|
||||
| `Bug #1394 <https://redmine.postgresql.org/issues/1394>`_ - Fix Table dialogue column specification issues
|
||||
| `Bug #1432 <https://redmine.postgresql.org/issues/1432>`_ - Enhanced OSX File Browser
|
||||
| `Bug #1585 <https://redmine.postgresql.org/issues/1585>`_ - Cannot save scripts to the network
|
||||
| `Bug #1599 <https://redmine.postgresql.org/issues/1599>`_ - Ensure the grant wizard works with objects with special characters in the name
|
||||
| `Bug #1603 <https://redmine.postgresql.org/issues/1603>`_ - Fix quoting of objects names for external utilities.
|
||||
| `Bug #1679 <https://redmine.postgresql.org/issues/1679>`_ - Re-engineer the background process executor to avoid using sqlite as some builds of components it relies on do not support working in forked children
|
||||
| `Bug #1680 <https://redmine.postgresql.org/issues/1680>`_ - Render column headers at the correct width in the query tool under Firefox
|
||||
| `Bug #1729 <https://redmine.postgresql.org/issues/1729>`_ - Improve display of role options
|
||||
| `Bug #1730 <https://redmine.postgresql.org/issues/1730>`_ - Improve the display of role membership on both the properties panel and role dialogue
|
||||
| `Bug #1745 <https://redmine.postgresql.org/issues/1745>`_ - Ensure breakpoints are cleared properly when working with Debugger
|
||||
| `Bug #1747 <https://redmine.postgresql.org/issues/1747>`_ - Add newly created triggers to the treeview
|
||||
| `Bug #1780 <https://redmine.postgresql.org/issues/1780>`_ - Properly size the SQL Editor gutter as the width of the line numbers increases
|
||||
| `Bug #1792 <https://redmine.postgresql.org/issues/1792>`_ - List files and folders alphabetically
|
||||
| `Bug #1800 <https://redmine.postgresql.org/issues/1800>`_ - Handle the template property on databases appropriately
|
||||
| `Bug #1801 <https://redmine.postgresql.org/issues/1801>`_ - Handle databases with datallowconn == false
|
||||
| `Bug #1807 <https://redmine.postgresql.org/issues/1807>`_ - Properly detect when files have changed in the query tool and set flag accordingly
|
||||
| `Bug #1830 <https://redmine.postgresql.org/issues/1830>`_ - Fix a SQL error when reverse-engineering ROLE SQL on EPAS servers
|
||||
| `Bug #1832 <https://redmine.postgresql.org/issues/1832>`_ - Prevent attempts to access what may be an empty list in Dependancies tab
|
||||
| `Bug #1840 <https://redmine.postgresql.org/issues/1840>`_ - Enable/disable NULLs and ASC/DESC options for index columns and exclusion constraints appropriately
|
||||
| `Bug #1842 <https://redmine.postgresql.org/issues/1842>`_ - Show index columns in the correct order in RE-SQL
|
||||
| `Bug #1855 <https://redmine.postgresql.org/issues/1855>`_ - Ensure dialogue panels show their errors themselves, and not in the properties panel when creating Trigger Function
|
||||
| `Bug #1865 <https://redmine.postgresql.org/issues/1865>`_ - Properly schema qualify domains when reverse engineering SQL
|
||||
| `Bug #1874 <https://redmine.postgresql.org/issues/1874>`_ - Add file resources to the windows runtime
|
||||
| `Bug #1893 <https://redmine.postgresql.org/issues/1893>`_ - Fix refreshing of Unique constraints
|
||||
| `Bug #1896 <https://redmine.postgresql.org/issues/1896>`_ - Use the correct OID for retrieving properties of freshly created exclusion constraints
|
||||
| `Bug #1899 <https://redmine.postgresql.org/issues/1899>`_ - Properly quote role names when specifying function ownership
|
||||
| `Bug #1909 <https://redmine.postgresql.org/issues/1909>`_ - Handle startup errors more gracefully in the runtime
|
||||
| `Bug #1912 <https://redmine.postgresql.org/issues/1912>`_ - Properly format arguments passed by triggers to functions
|
||||
| `Bug #1919 <https://redmine.postgresql.org/issues/1919>`_ - Ensure all changes to rows are stored in the data editor
|
||||
| `Bug #1924 <https://redmine.postgresql.org/issues/1924>`_ - Ensure the check_option is only set when editing views when appropriate
|
||||
| `Bug #1936 <https://redmine.postgresql.org/issues/1936>`_ - Don't strip \r\n from "Download as CSV" batches of rows, as it leads to malformed data
|
||||
| `Bug #1937 <https://redmine.postgresql.org/issues/1937>`_ - Generate mSQL for new schemas correctly
|
||||
| `Bug #1938 <https://redmine.postgresql.org/issues/1938>`_ - Fix sorting of numerics in the statistics grids
|
||||
| `Bug #1939 <https://redmine.postgresql.org/issues/1939>`_ - Updated dynamic default for the window size (90% x 90%)
|
||||
| `Bug #1949 <https://redmine.postgresql.org/issues/1949>`_ - Ensure trigger function names are schema qualified in trigger RE-SQL
|
||||
| `Bug #1951 <https://redmine.postgresql.org/issues/1951>`_ - Fix issue where nnable to browse table columns when oid values exceeed max int
|
||||
| `Bug #1953 <https://redmine.postgresql.org/issues/1953>`_ - Add display messages and notices received in the query tool
|
||||
| `Bug #1961 <https://redmine.postgresql.org/issues/1961>`_ - Fix upgrade check on Python 3
|
||||
| `Bug #1962 <https://redmine.postgresql.org/issues/1962>`_ - Ensure treeview collection nodes are translated in the UI
|
||||
| `Bug #1967 <https://redmine.postgresql.org/issues/1967>`_ - Store layout changes on each adjustment
|
||||
| `Bug #1976 <https://redmine.postgresql.org/issues/1976>`_ - Prevent users selecting elements of the UI that shouldn't be selectable
|
||||
| `Bug #1979 <https://redmine.postgresql.org/issues/1979>`_ - Deal with Function arguments correctly in the properties dialogue
|
||||
| `Bug #1986 <https://redmine.postgresql.org/issues/1986>`_ - Fix various encoding issues with multibyte paths and filenames resulting in empty file save
|
||||
| `Bug #1992 <https://redmine.postgresql.org/issues/1992>`_ - Quote identifiers correctly in auto-complete
|
||||
| `Bug #1994 <https://redmine.postgresql.org/issues/1994>`_ - Update to show modifications in edit grid
|
||||
| `Bug #2000 <https://redmine.postgresql.org/issues/2000>`_ - Allow setting of effective_io_concurrency on tablespaces in 9.6+
|
||||
| `Bug #2005 <https://redmine.postgresql.org/issues/2005>`_ - Fix various mis-spellings of VACUUM
|
||||
| `Bug #2006 <https://redmine.postgresql.org/issues/2006>`_ - Fix error when modifying table name or set schema on tables with postgis geometry column
|
||||
| `Bug #2007 <https://redmine.postgresql.org/issues/2007>`_ - Correctly sort rows by the pkey when viewing first/last 100
|
||||
| `Bug #2009 <https://redmine.postgresql.org/issues/2009>`_ - Reset the column list properly if the access method is changed on an index to ensure error handling works correctly
|
||||
| `Bug #2012 <https://redmine.postgresql.org/issues/2012>`_ - Prevent attempts to create server groups with no name
|
||||
| `Bug #2015 <https://redmine.postgresql.org/issues/2015>`_ - Enable trigger option when user tries to change Row trigger value through properties section
|
||||
| `Bug #2024 <https://redmine.postgresql.org/issues/2024>`_ - Properly handle setting comments and other options on databases with allowconn = False
|
||||
| `Bug #2026 <https://redmine.postgresql.org/issues/2026>`_ - Improve detection of the pldbgapi extension and functions before allowing debugging
|
||||
| `Bug #2027 <https://redmine.postgresql.org/issues/2027>`_ - Fix inconsistent table styling
|
||||
| `Bug #2028 <https://redmine.postgresql.org/issues/2028>`_ - Fix display of double scrollbars on the grant wizard
|
||||
| `Bug #2032 <https://redmine.postgresql.org/issues/2032>`_ - Fix time formatting on dashboards
|
||||
| `Bug #2033 <https://redmine.postgresql.org/issues/2033>`_ - Show icons for unique and exclusion constraints in the dependency/dependents panels
|
||||
| `Bug #2045 <https://redmine.postgresql.org/issues/2045>`_ - Update copyright year on doc page
|
||||
| `Bug #2046 <https://redmine.postgresql.org/issues/2046>`_ - Fix error when setting up regression on Windows for pgadmin4
|
||||
| `Bug #2047 <https://redmine.postgresql.org/issues/2047>`_ - Ensure dialogues cannot be moved under the navbar
|
||||
| `Bug #2061 <https://redmine.postgresql.org/issues/2061>`_ - Enable/disable NULLs and ASC/DESC options for index columns and exclusion constraints appropriately
|
||||
| `Bug #2065 <https://redmine.postgresql.org/issues/2065>`_ - Improve display of columns of exclusion contraints and foreign keys in the properties lists
|
||||
| `Bug #2069 <https://redmine.postgresql.org/issues/2069>`_ - Correct tablespace displayed in table properties
|
||||
| `Bug #2076 <https://redmine.postgresql.org/issues/2076>`_ - Handle sized time/timestamp columns correctly
|
||||
| `Bug #2109 <https://redmine.postgresql.org/issues/2109>`_ - Update copyright year
|
||||
| `Bug #2110 <https://redmine.postgresql.org/issues/2110>`_ - Handle saved directories that no longer exist gracefully
|
||||
| `Bug #2112 <https://redmine.postgresql.org/issues/2026>`_ - Enable comments on Initial database through right Click
|
||||
| `Bug #2133 <https://redmine.postgresql.org/issues/2133>`_ - Fix display of graphical query plans for UPDATE/DELETE queries
|
||||
| `Bug #2138 <https://redmine.postgresql.org/issues/2138>`_ - Fix display of zeros in read-only grid editors
|
||||
| `Bug #2139 <https://redmine.postgresql.org/issues/2139>`_ - Fixed issue causing Message (Connection to the server has been lost.) displayed with Materialized view and view under sql tab
|
||||
| `Bug #2152 <https://redmine.postgresql.org/issues/2152>`_ - Fix handling of "char" columns
|
||||
| `Bug #2156 <https://redmine.postgresql.org/issues/2156>`_ - Added compatibility fixes for newer versions of Jinja2 (e.g. 2.9.5+)
|
||||
@@ -0,0 +1,49 @@
|
||||
***********
|
||||
Version 1.3
|
||||
***********
|
||||
|
||||
Release date: 2017-03-10
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 1.2.
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #2036 <https://redmine.postgresql.org/issues/2036>`_ - Query tool efficiency - SlickGrid result set format efficiency
|
||||
| `Feature #2038 <https://redmine.postgresql.org/issues/2038>`_ - Query tool efficiency - Incremental back off when polling
|
||||
| `Feature #2163 <https://redmine.postgresql.org/issues/2163>`_ - Make syntax highlighting more visible
|
||||
| `Feature #2210 <https://redmine.postgresql.org/issues/2210>`_ - Build a universal Python wheel instead of per-python-version ones
|
||||
| `Feature #2215 <https://redmine.postgresql.org/issues/2215>`_ - Improve visibility of syntax highlighting colours
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #1796 <https://redmine.postgresql.org/issues/1796>`_ - Add missing "Run Now" option for pgAdmin jobs
|
||||
| `Bug #1797 <https://redmine.postgresql.org/issues/1797>`_ - Resolve encoding issues with DATA_DIR
|
||||
| `Bug #1914 <https://redmine.postgresql.org/issues/1914>`_ - Resolved error utf8' codec can't decode byte
|
||||
| `Bug #1983 <https://redmine.postgresql.org/issues/1983>`_ - Fix bug in Sql query contains Arabic Charaters
|
||||
| `Bug #2089 <https://redmine.postgresql.org/issues/2089>`_ - Add PARALLEL SAFE|UNSAFE|RESTRICTED support
|
||||
| `Bug #2115 <https://redmine.postgresql.org/issues/2115>`_ - Fix exclusion constraint reverse engineered SQL
|
||||
| `Bug #2119 <https://redmine.postgresql.org/issues/2119>`_ - Fix display of long integers and decimals
|
||||
| `Bug #2126 <https://redmine.postgresql.org/issues/2126>`_ - Correct node labels in Preferences for EDB functions and procedures
|
||||
| `Bug #2151 <https://redmine.postgresql.org/issues/2151>`_ - Display un-sized varlen column types correctly in the query tool
|
||||
| `Bug #2154 <https://redmine.postgresql.org/issues/2154>`_ - Fix display of long integers and decimals
|
||||
| `Bug #2159 <https://redmine.postgresql.org/issues/2159>`_ - Resolve issue where Query editor is not working with Python2.6
|
||||
| `Bug #2160 <https://redmine.postgresql.org/issues/2160>`_ - Various encoding fixes to allow 'ascii' codec to decode byte 0xc3 in position 66: ordinal not in range(128)
|
||||
| `Bug #2166 <https://redmine.postgresql.org/issues/2166>`_ - Resolved import/Export issue for a table
|
||||
| `Bug #2173 <https://redmine.postgresql.org/issues/2173>`_ - Resolved issues where Sequences API test cases are not working in PG9.2 and PPAS9.2
|
||||
| `Bug #2174 <https://redmine.postgresql.org/issues/2174>`_ - Resolved various file-system encoding/decoding related cases
|
||||
| `Bug #2185 <https://redmine.postgresql.org/issues/2185>`_ - Removed sorting columns on the treeview
|
||||
| `Bug #2192 <https://redmine.postgresql.org/issues/2192>`_ - Fix startup complete tests to ensure we properly poll the server for completed startup
|
||||
| `Bug #2198 <https://redmine.postgresql.org/issues/2198>`_ - Fix function arguments when generating create SQL
|
||||
| `Bug #2200 <https://redmine.postgresql.org/issues/2200>`_ - Properly handle event trigger functions in different schemas
|
||||
| `Bug #2201 <https://redmine.postgresql.org/issues/2201>`_ - Fix renaming of check constraints when the table name is changed at the same time
|
||||
| `Bug #2202 <https://redmine.postgresql.org/issues/2202>`_ - Fix issue where Dependents query fails due to non ascii characters
|
||||
| `Bug #2204 <https://redmine.postgresql.org/issues/2204>`_ - Fixed issue where pgadmin 4 jobs not showing any activity
|
||||
| `Bug #2205 <https://redmine.postgresql.org/issues/2205>`_ - Fix display of boolean nulls in the query tool
|
||||
| `Bug #2208 <https://redmine.postgresql.org/issues/2208>`_ - Ensure primary key column names are quoted in View Data mode of the Query Tool
|
||||
| `Bug #2212 <https://redmine.postgresql.org/issues/2212>`_ - Ensure servers are deleted when their parent group is deleted
|
||||
| `Bug #2213 <https://redmine.postgresql.org/issues/2213>`_ - Enable right click on browser tree
|
||||
| `Bug #2218 <https://redmine.postgresql.org/issues/2218>`_ - Show the correct indeterminate state when editing new boolean values
|
||||
| `Bug #2228 <https://redmine.postgresql.org/issues/2228>`_ - Authenticate the runtime to the server
|
||||
| `Bug #2230 <https://redmine.postgresql.org/issues/2230>`_ - Prevent the Slonik logo obscuring the login dialogue on small displays in server mode
|
||||
@@ -0,0 +1,45 @@
|
||||
***********
|
||||
Version 1.4
|
||||
***********
|
||||
|
||||
Release date: 2017-04-13
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 1.3.
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #2232 <https://redmine.postgresql.org/issues/2232>`_ - Add the ability to gray-out/disable the "Save Password" option when creating a connection to a server
|
||||
| `Feature #2261 <https://redmine.postgresql.org/issues/2261>`_ - Display table DDL for Greenplum in SQL tab
|
||||
| `Feature #2320 <https://redmine.postgresql.org/issues/2163>`_ - Added German translation
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #2077 <https://redmine.postgresql.org/issues/2077>`_ - Add missing "Run Now" option for pgAdmin jobs
|
||||
| `Bug #2105 <https://redmine.postgresql.org/issues/2105>`_ - Fix validation on the table dialogue so the Save button isn't enabled if the name is removed and autovac custom settings are enabled
|
||||
| `Bug #2145 <https://redmine.postgresql.org/issues/2145>`_ - Resolve the issue for restoring the table from the backup
|
||||
| `Bug #2187 <https://redmine.postgresql.org/issues/2187>`_ - Ensure the web/ directory is cleared before upgrading Windows installations
|
||||
| `Bug #2190 <https://redmine.postgresql.org/issues/2190>`_ - Allow users to select UI language at login or from Preferences rather than unpredictable behaviour from browsers
|
||||
| `Bug #2226 <https://redmine.postgresql.org/issues/2226>`_ - Show tooltips for disabled buttons to help user learning
|
||||
| `Bug #2241 <https://redmine.postgresql.org/issues/2241>`_ - Fix numeric control validation in nested schemas
|
||||
| `Bug #2243 <https://redmine.postgresql.org/issues/2243>`_ - Fix dropping of databases with Unicode names
|
||||
| `Bug #2244 <https://redmine.postgresql.org/issues/2244>`_ - Prevent an error being displayed if the user views data on a table with no columns
|
||||
| `Bug #2246 <https://redmine.postgresql.org/issues/2246>`_ - Add missing braces to reverse engineered SQL header block for Functions
|
||||
| `Bug #2258 <https://redmine.postgresql.org/issues/2258>`_ - Fix handling of DATERANGE[] type
|
||||
| `Bug #2264 <https://redmine.postgresql.org/issues/2264>`_ - Resolve error message *ExtDeprecationWarning* displayed on new pgAdmin4 setup for Python 3.4 on ubuntu 14.04 Linux 64
|
||||
| `Bug #2265 <https://redmine.postgresql.org/issues/2265>`_ - Resolved import/Export issue for a table
|
||||
| `Bug #2274 <https://redmine.postgresql.org/issues/2274>`_ - Properly handle truncated table names
|
||||
| `Bug #2277 <https://redmine.postgresql.org/issues/2277>`_ - Resolved various file-system encoding/decoding related cases
|
||||
| `Bug #2281 <https://redmine.postgresql.org/issues/2281>`_ - Ensure menus are updated after disconnecting a server
|
||||
| `Bug #2283 <https://redmine.postgresql.org/issues/2283>`_ - Check if cell is in multiselect mode before setting default selection of multiple values
|
||||
| `Bug #2287 <https://redmine.postgresql.org/issues/2287>`_ - Properly handle EXPLAIN queries entered directly by the user in the query tool
|
||||
| `Bug #2291 <https://redmine.postgresql.org/issues/2291>`_ - Fix error highlighting in the query tool
|
||||
| `Bug #2299 <https://redmine.postgresql.org/issues/2299>`_ - Fix usage of QString
|
||||
| `Bug #2303 <https://redmine.postgresql.org/issues/2303>`_ - Fix ascending/descending sort order in backgrid while clicking on the headers
|
||||
| `Bug #2304 <https://redmine.postgresql.org/issues/2304>`_ - Resolve the issue for restoring the table from the backup
|
||||
| `Bug #2305 <https://redmine.postgresql.org/issues/2305>`_ - Resolve the issue where Generic function qtLiteral was not adapting values properly when they contain non ascii characters
|
||||
| `Bug #2310 <https://redmine.postgresql.org/issues/2310>`_ - Fix Dialog Help where query tool/Debugger opens in new browser tab
|
||||
| `Bug #2319 <https://redmine.postgresql.org/issues/2319>`_ - Resolve issue where Click on pgAdmin4 logo leads to unauthorized error
|
||||
| `Bug #2321 <https://redmine.postgresql.org/issues/2321>`_ - Improved functionality of browser tree when adding new nodes if parent collection node has not loaded
|
||||
| `Bug #2330 <https://redmine.postgresql.org/issues/2330>`_ - Ensure the query tool displays but does not render HTML returned by the server in the results grid
|
||||
@@ -0,0 +1,37 @@
|
||||
***********
|
||||
Version 1.5
|
||||
***********
|
||||
|
||||
Release date: 2017-05-19
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 1.4.
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #2216 <https://redmine.postgresql.org/issues/2216>`_ - Allow column or row selection in the query tool
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #2225 <https://redmine.postgresql.org/issues/2225>`_ - Hide menu options for creating objects, if the object type is set to hidden. Includes Jasmine tests
|
||||
| `Bug #2253 <https://redmine.postgresql.org/issues/2253>`_ - Fix various issues in CSV file download feature
|
||||
| `Bug #2257 <https://redmine.postgresql.org/issues/2257>`_ - Improve handling of nulls and default values in the data editor
|
||||
| `Bug #2271 <https://redmine.postgresql.org/issues/2271>`_ - Don't change the trigger icon back to "enabled" when the trigger is updated when it's disabled
|
||||
| `Bug #2284 <https://redmine.postgresql.org/issues/2284>`_ - Allow creation of tables with pure numeric names
|
||||
| `Bug #2292 <https://redmine.postgresql.org/issues/2292>`_ - Only reconnect to databases that were previously connected
|
||||
| `Bug #2314 <https://redmine.postgresql.org/issues/2314>`_ - Fix various issues in CSV file download feature
|
||||
| `Bug #2315 <https://redmine.postgresql.org/issues/2315>`_ - Fix sorting of sizes on the statistics views by sorting raw values and prettifying on the client side. Includes Jasmine tests for the prettyfying function
|
||||
| `Bug #2318 <https://redmine.postgresql.org/issues/2318>`_ - Order foreign table columns correctly
|
||||
| `Bug #2331 <https://redmine.postgresql.org/issues/2331>`_ - Fix binary search algorithm so new treeview nodes are added in the correct position
|
||||
| `Bug #2336 <https://redmine.postgresql.org/issues/2336>`_ - Update inode info when refreshing treeview nodes.
|
||||
| `Bug #2339 <https://redmine.postgresql.org/issues/2339>`_ - Ensure the treeview can be scrolled horizontally
|
||||
| `Bug #2350 <https://redmine.postgresql.org/issues/2350>`_ - Fix handling of default parameters ordering in functions
|
||||
| `Bug #2354 <https://redmine.postgresql.org/issues/2354>`_ - Fix the Backup module where it was not working if user changes its preference language other than English
|
||||
| `Bug #2356 <https://redmine.postgresql.org/issues/2356>`_ - Ensure errors thrown when deleting rows in the query tool in edit mode are shown properly
|
||||
| `Bug #2360 <https://redmine.postgresql.org/issues/2360>`_ - Fix various issues in CSV file download feature
|
||||
| `Bug #2369 <https://redmine.postgresql.org/issues/2369>`_ - Support loading files with Unicode BOMs
|
||||
| `Bug #2377 <https://redmine.postgresql.org/issues/2377>`_ - Update psycopg2 version for PostgreSQL 10 compatibility
|
||||
| `Bug #2379 <https://redmine.postgresql.org/issues/2379>`_ - Make various improvements to the NULL/DEFAULT handling in the data editor
|
||||
| `Bug #2405 <https://redmine.postgresql.org/issues/2405>`_ - Ensure object names are properly escaped for external process management
|
||||
| `Bug #2410 <https://redmine.postgresql.org/issues/2410>`_ - Fix PostgreSQL 10.0 compatibility issues
|
||||
@@ -0,0 +1,103 @@
|
||||
***********
|
||||
Version 1.6
|
||||
***********
|
||||
|
||||
Release date: 2017-07-13
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 1.5
|
||||
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #1344 <https://redmine.postgresql.org/issues/1344>`_ - Allow the Query Tool, Debugger and web browser tabs to be moved to different monitors as desired
|
||||
| `Feature #1533 <https://redmine.postgresql.org/issues/1533>`_ - Set focus on the first enabled field when a dialogue is opened
|
||||
| `Feature #1535 <https://redmine.postgresql.org/issues/1535>`_ - Teach dialogues about Escape to cancel, Enter to Save/OK, and F1 for help
|
||||
| `Feature #1971 <https://redmine.postgresql.org/issues/1971>`_ - Retain column sizing in the query tool results grid when the same query is re-run multiple times in a row
|
||||
| `Feature #1972 <https://redmine.postgresql.org/issues/1972>`_ - Prompt the user to save dirty queries rather than discard them for a more natural workflow
|
||||
| `Feature #2137 <https://redmine.postgresql.org/issues/2137>`_ - On-demand loading for the query tool results
|
||||
| `Feature #2191 <https://redmine.postgresql.org/issues/2191>`_ - Add support for the hostaddr connection parameter. This helps us play nicely with Kerberos/SSPI and friends
|
||||
| `Feature #2282 <https://redmine.postgresql.org/issues/2282>`_ - Overhaul the query history tab to allow browsing of the history and full query text
|
||||
| `Feature #2379 <https://redmine.postgresql.org/issues/2379>`_ - Support inserting multiple new rows into a table without clicking Save for each row
|
||||
| `Feature #2485 <https://redmine.postgresql.org/issues/2485>`_ - Add a shortcut to reset the zoom level in the runtime
|
||||
| `Feature #2506 <https://redmine.postgresql.org/issues/2506>`_ - Allow the user to close the dashboard panel
|
||||
| `Feature #2513 <https://redmine.postgresql.org/issues/2513>`_ - Add preferences to enable brace matching and brace closing in the SQL editors
|
||||
|
||||
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #1126 <https://redmine.postgresql.org/issues/1126>`_ - Various FTS dictionary cleanups
|
||||
| `Bug #1229 <https://redmine.postgresql.org/issues/1229>`_ - Fix default values and SQL formatting for event triggers
|
||||
| `Bug #1466 <https://redmine.postgresql.org/issues/1466>`_ - Prevent attempts to debug procedures with variadic arguments
|
||||
| `Bug #1525 <https://redmine.postgresql.org/issues/1525>`_ - Make $ quoting consistent
|
||||
| `Bug #1575 <https://redmine.postgresql.org/issues/1575>`_ - Properly display security labels on EPAS 9.2+
|
||||
| `Bug #1795 <https://redmine.postgresql.org/issues/1795>`_ - Fix validation for external and range types
|
||||
| `Bug #1813 <https://redmine.postgresql.org/issues/1813>`_ - List packages in PPAS 9.2-9.4 when creating synonyms
|
||||
| `Bug #1831 <https://redmine.postgresql.org/issues/1831>`_ - Fix server stats display for EPAS 9.2, where inet needs casting to text for concatenation
|
||||
| `Bug #1851 <https://redmine.postgresql.org/issues/1851>`_ - Reverse engineer SQL for table-returning functions correctly
|
||||
| `Bug #1860 <https://redmine.postgresql.org/issues/1860>`_ - Ensure default values are honoured when adding/editing columns
|
||||
| `Bug #1888 <https://redmine.postgresql.org/issues/1888>`_ - Fix various issues with pgAgent job steps and schedules
|
||||
| `Bug #1889 <https://redmine.postgresql.org/issues/1889>`_ - Fix various issues with pgAgent job steps and schedules
|
||||
| `Bug #1890 <https://redmine.postgresql.org/issues/1890>`_ - Fix various issues with pgAgent job steps and schedules
|
||||
| `Bug #1920 <https://redmine.postgresql.org/issues/1920>`_ - Ensure saved passwords are effective immediately, not just following a restart when first saved
|
||||
| `Bug #1928 <https://redmine.postgresql.org/issues/1928>`_ - Fix the handling of double precision[] type
|
||||
| `Bug #1934 <https://redmine.postgresql.org/issues/1934>`_ - Fix import/export to work as expected with TSV data
|
||||
| `Bug #1999 <https://redmine.postgresql.org/issues/1999>`_ - Handle warning correctly when saving query results to an unmounted USB drive
|
||||
| `Bug #2013 <https://redmine.postgresql.org/issues/2013>`_ - Increase the default size of the Grant Wizard to enable it to properly display privileges at the default size on smaller displays
|
||||
| `Bug #2014 <https://redmine.postgresql.org/issues/2014>`_ - To fix unexpected behaviour displayed if user stops debugging on package/procedure fire_emp
|
||||
| `Bug #2043 <https://redmine.postgresql.org/issues/2043>`_ - Properly handle trigger functions with parameters
|
||||
| `Bug #2078 <https://redmine.postgresql.org/issues/2078>`_ - Refresh the SQL editor view on resize to ensure the contents are re-rendered for the new viewport
|
||||
| `Bug #2086 <https://redmine.postgresql.org/issues/2086>`_ - Allow editing of the WITH ADMIN option of role membership
|
||||
| `Bug #2113 <https://redmine.postgresql.org/issues/2113>`_ - Correct the validation logic when modifying indexes/exclusion constraints
|
||||
| `Bug #2116 <https://redmine.postgresql.org/issues/2116>`_ - Enable dialogue help buttons on Language and Foreign Table dialogues
|
||||
| `Bug #2142 <https://redmine.postgresql.org/issues/2142>`_ - Fix canceling of Grant Wizard on Windows
|
||||
| `Bug #2155 <https://redmine.postgresql.org/issues/2155>`_ - Fix removal of sizes from column definitions
|
||||
| `Bug #2162 <https://redmine.postgresql.org/issues/2162>`_ - Allow non-superusers to debug their own functions and prevent them from setting global breakpoints
|
||||
| `Bug #2242 <https://redmine.postgresql.org/issues/2242>`_ - Fix an issue in NodeAjaxControl caching with cache-node field and add cache-node field in Trigger & Event trigger node so that whenever the user creates new Trigger Function we get new data from server in NodeAjaxControl
|
||||
| `Bug #2280 <https://redmine.postgresql.org/issues/2280>`_ - Handle procedure flags (IMMUTABLE STRICT SECURITY DEFINER PARALLEL RESTRICTED) properly in RE-SQL on EPAS
|
||||
| `Bug #2324 <https://redmine.postgresql.org/issues/2324>`_ - Fix the PostGIS Datatypes in SQL tab, Create / Update dialogues for Table, Column, Foreign Table and Type node
|
||||
| `Bug #2344 <https://redmine.postgresql.org/issues/2344>`_ - Fix issue with ctrl-c / ctrl-v not working in query tool
|
||||
| `Bug #2348 <https://redmine.postgresql.org/issues/2348>`_ - Fix issue when resizing columns in Query Too/View Data where all row/colums will select/deselect
|
||||
| `Bug #2355 <https://redmine.postgresql.org/issues/2355>`_ - Properly refresh the parent node when renaming children
|
||||
| `Bug #2357 <https://redmine.postgresql.org/issues/2355>`_ - Cache statistics more reliably
|
||||
| `Bug #2381 <https://redmine.postgresql.org/issues/2381>`_ - Fix the RE-SQL for for views to properly qualify trigger function names
|
||||
| `Bug #2386 <https://redmine.postgresql.org/issues/2386>`_ - Display and allow toggling of trigger enable/disable status from the trigger dialogue
|
||||
| `Bug #2398 <https://redmine.postgresql.org/issues/2398>`_ - Bypass the proxy server for local addresses on Windows
|
||||
| `Bug #2400 <https://redmine.postgresql.org/issues/2400>`_ - Cleanup handling of default/null values when data editing
|
||||
| `Bug #2414 <https://redmine.postgresql.org/issues/2414>`_ - Improve error handling in cases where the user tries to rename or create a server group that would duplicate an existing group
|
||||
| `Bug #2417 <https://redmine.postgresql.org/issues/2417>`_ - Order columns in multi-column pkeys correctly
|
||||
| `Bug #2422 <https://redmine.postgresql.org/issues/2422>`_ - Fix RE-SQL for rules which got the table name wrong in the header and DROP statement
|
||||
| `Bug #2425 <https://redmine.postgresql.org/issues/2425>`_ - Handle composite primary keys correctly when deleting rows in the Edit Grid
|
||||
| `Bug #2426 <https://redmine.postgresql.org/issues/2426>`_ - Allow creation of ENUM types with no members
|
||||
| `Bug #2427 <https://redmine.postgresql.org/issues/2427>`_ - Add numerous missing checks to ensure objects really exist when we think they do
|
||||
| `Bug #2435 <https://redmine.postgresql.org/issues/2435>`_ - Pass the database ID to the query tool when using the Script options
|
||||
| `Bug #2436 <https://redmine.postgresql.org/issues/2436>`_ - Ensure the last placeholder is included when generating UPDATE scripts for tables
|
||||
| `Bug #2448 <https://redmine.postgresql.org/issues/2448>`_ - Ensure that boolean checkboxes cycle values in the correct order
|
||||
| `Bug #2450 <https://redmine.postgresql.org/issues/2450>`_ - Fix error on the stats tab with PG10. Also, rename the 10.0_plus template directory to 10_plus to match the new versioning
|
||||
| `Bug #2461 <https://redmine.postgresql.org/issues/2461>`_ - Allow users to remove default values from columns properly
|
||||
| `Bug #2468 <https://redmine.postgresql.org/issues/2468>`_ - Fix issue where function create script won't compile
|
||||
| `Bug #2470 <https://redmine.postgresql.org/issues/2470>`_ - Fix an intermittent error seen during result polling
|
||||
| `Bug #2476 <https://redmine.postgresql.org/issues/2476>`_ - Improvements to the Query Results grid including improvements to the UI and allow copy/paste from sets of rows, columns or arbitrary blocks of cells
|
||||
| `Bug #2477 <https://redmine.postgresql.org/issues/2477>`_ - Ensure text editors render in an appropriate place on the results grid
|
||||
| `Bug #2479 <https://redmine.postgresql.org/issues/2479>`_ - No need for the menu icon to link to the homepage, as pgAdmin is a SPA
|
||||
| `Bug #2482 <https://redmine.postgresql.org/issues/2482>`_ - Use a more sensible name for Query Tool tabs
|
||||
| `Bug #2486 <https://redmine.postgresql.org/issues/2486>`_ - Ensure the feature tests use the correct test settings database
|
||||
| `Bug #2487 <https://redmine.postgresql.org/issues/2487>`_ - Maintain a client-side cache of preference values, populated using an async call
|
||||
| `Bug #2489 <https://redmine.postgresql.org/issues/2489>`_ - Fix clipboard handling with large datasets
|
||||
| `Bug #2492 <https://redmine.postgresql.org/issues/2492>`_ - Ensure the initial password is properly hashed during setup in web mode
|
||||
| `Bug #2498 <https://redmine.postgresql.org/issues/2498>`_ - Properly handle bytea[], and 'infinity'::real/real[]
|
||||
| `Bug #2502 <https://redmine.postgresql.org/issues/2502>`_ - Properly handle bytea[], and 'infinity'::real/real[]
|
||||
| `Bug #2503 <https://redmine.postgresql.org/issues/2503>`_ - Handle missing/dropped synonyms gracefully
|
||||
| `Bug #2504 <https://redmine.postgresql.org/issues/2504>`_ - Update MatView and pgAgent modules to work with recent integer/numeric changes
|
||||
| `Bug #2507 <https://redmine.postgresql.org/issues/2507>`_ - Ensure revoked public privileges are displayed in the RE-SQL for functions
|
||||
| `Bug #2518 <https://redmine.postgresql.org/issues/2518>`_ - Fix encoding issue when saving servers
|
||||
| `Bug #2522 <https://redmine.postgresql.org/issues/2522>`_ - Improve speed of Select All in the results grid
|
||||
| `Bug #2527 <https://redmine.postgresql.org/issues/2527>`_ - Fix deletion of table rows with the column definition having NOT NULL TRUE and HAS NO DEFAULT VALUE
|
||||
| `Bug #2528 <https://redmine.postgresql.org/issues/2528>`_ - Allow breakpoints to be set on triggers on views
|
||||
| `Bug #2529 <https://redmine.postgresql.org/issues/2529>`_ - Resolve a number of issues with domains and domain constraints
|
||||
| `Bug #2532 <https://redmine.postgresql.org/issues/2532>`_ - Refresh nodes correctly when there is a single child that is updated
|
||||
| `Bug #2534 <https://redmine.postgresql.org/issues/2534>`_ - Fix handling of CREATE TABLE OF <type>
|
||||
| `Bug #2535 <https://redmine.postgresql.org/issues/2535>`_ - Fix clear history functionality
|
||||
| `Bug #2540 <https://redmine.postgresql.org/issues/2540>`_ - Ensure the save password option is enabled when creating a server
|
||||
@@ -0,0 +1,90 @@
|
||||
***********
|
||||
Version 2.0
|
||||
***********
|
||||
|
||||
Release date: 2017-09-xx
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 1.6
|
||||
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #1918 <https://redmine.postgresql.org/issues/1918>`_ - Add a field to the Server Dialogue allowing users to specify a subset of databases they'd like to see in the treeview
|
||||
| `Feature #2135 <https://redmine.postgresql.org/issues/2135>`_ - Significantly speed up loading of the application
|
||||
| `Feature #2556 <https://redmine.postgresql.org/issues/2556>`_ - Allow for slow vs. fast connection failures
|
||||
| `Feature #2579 <https://redmine.postgresql.org/issues/2579>`_ - Default the file browser view to list, and make it configurable
|
||||
| `Feature #2597 <https://redmine.postgresql.org/issues/2597>`_ - Allow queries to be cancelled from the dashboard and display additional info in the subnode control
|
||||
| `Feature #2649 <https://redmine.postgresql.org/issues/2649>`_ - Support use of SSL certificates for authentication
|
||||
| `Feature #2650 <https://redmine.postgresql.org/issues/2650>`_ - Support use of pgpass files
|
||||
| `Feature #2662 <https://redmine.postgresql.org/issues/2662>`_ - Ship with pre-configured paths that can work in both Server and Desktop modes out of the box
|
||||
| `Feature #2689 <https://redmine.postgresql.org/issues/2689>`_ - Update icons with new designs and remove from menus to de-clutter the UI
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #1165 <https://redmine.postgresql.org/issues/1165>`_ - Prevent continual polling for graph data on the dashboard if the server is disconnected
|
||||
| `Bug #1697 <https://redmine.postgresql.org/issues/1697>`_ - Update CodeMirror version
|
||||
| `Bug #2043 <https://redmine.postgresql.org/issues/2043>`_ - Properly handle trigger functions with parameters
|
||||
| `Bug #2074 <https://redmine.postgresql.org/issues/2074>`_ - Make $ quoting consistent
|
||||
| `Bug #2080 <https://redmine.postgresql.org/issues/2080>`_ - Fix issue where Browser hangs/crashes when loading data (using sql editor) from table which contains large blob data
|
||||
| `Bug #2153 <https://redmine.postgresql.org/issues/2153>`_ - Fix handline of large file uploads and properly show any errors that may occur
|
||||
| `Bug #2168 <https://redmine.postgresql.org/issues/2168>`_ - Update CodeMirror version
|
||||
| `Bug #2170 <https://redmine.postgresql.org/issues/2170>`_ - Support SSL in the regression tests
|
||||
| `Bug #2324 <https://redmine.postgresql.org/issues/2324>`_ - Fix PostGIS Datatypes in SQL tab, Create / Update dialogues for Table, Column, Foreign Table and Type node
|
||||
| `Bug #2447 <https://redmine.postgresql.org/issues/2447>`_ - Update CodeMirror version
|
||||
| `Bug #2452 <https://redmine.postgresql.org/issues/2452>`_ - Install pgadmin4-v1 1.5 on Centos7
|
||||
| `Bug #2501 <https://redmine.postgresql.org/issues/2501>`_ - Fix collation tests on Windows, replace use of default 'POSIX' collation with 'C' collation for testing
|
||||
| `Bug #2541 <https://redmine.postgresql.org/issues/2541>`_ - Fix issues using special keys on MacOS
|
||||
| `Bug #2544 <https://redmine.postgresql.org/issues/2544>`_ - Correct malformed query generated when using custom type
|
||||
| `Bug #2551 <https://redmine.postgresql.org/issues/2551>`_ - Show tablespace on partitions
|
||||
| `Bug #2555 <https://redmine.postgresql.org/issues/2555>`_ - Fix issue in query tool where messages were not displaying from functions/procedures properly
|
||||
| `Bug #2557 <https://redmine.postgresql.org/issues/2557>`_ - Tidy up tab styling
|
||||
| `Bug #2558 <https://redmine.postgresql.org/issues/2558>`_ - Prevent the tab bar being hidden when detached tabs are being closed
|
||||
| `Bug #2559 <https://redmine.postgresql.org/issues/2559>`_ - Stop tool buttons from changing their styling unexpectedly
|
||||
| `Bug #2560 <https://redmine.postgresql.org/issues/2560>`_ - Fix View 'CREATE Script' Problem
|
||||
| `Bug #2562 <https://redmine.postgresql.org/issues/2562>`_ - Update CodeMirror version
|
||||
| `Bug #2563 <https://redmine.postgresql.org/issues/2563>`_ - Fix paths under non-standard virtual directories
|
||||
| `Bug #2566 <https://redmine.postgresql.org/issues/2566>`_ - Fix Pause/Resume Replay of WAL files for PostgreSQL 10
|
||||
| `Bug #2567 <https://redmine.postgresql.org/issues/2567>`_ - Use the proper database connection to fetch the default priviledges in the properties tab of the database
|
||||
| `Bug #2582 <https://redmine.postgresql.org/issues/2582>`_ - Unset compression ratio if it is an empty string in Backup module
|
||||
| `Bug #2586 <https://redmine.postgresql.org/issues/2586>`_ - Cleanup feature tests
|
||||
| `Bug #2590 <https://redmine.postgresql.org/issues/2590>`_ - Allow navigation of query history using the arrow keys
|
||||
| `Bug #2592 <https://redmine.postgresql.org/issues/2592>`_ - Stop Flask from initialising service twice in Debug mode
|
||||
| `Bug #2593 <https://redmine.postgresql.org/issues/2593>`_ - Ensure babel-polyfill is loaded in older qWebKits
|
||||
| `Bug #2594 <https://redmine.postgresql.org/issues/2594>`_ - Fix disconnection of new databases
|
||||
| `Bug #2596 <https://redmine.postgresql.org/issues/2596>`_ - Define the proper NODE_ENV environment during running the webpack
|
||||
| `Bug #2606 <https://redmine.postgresql.org/issues/2606>`_ - Ensure role names are escaped in the membership control
|
||||
| `Bug #2616 <https://redmine.postgresql.org/issues/2616>`_ - Domain create dialog do not open and Font size issue in Security label control
|
||||
| `Bug #2617 <https://redmine.postgresql.org/issues/2617>`_ - Add missing pgagent file in webpack.config.js
|
||||
| `Bug #2619 <https://redmine.postgresql.org/issues/2619>`_ - Fix quoting of index column names on tables
|
||||
| `Bug #2620 <https://redmine.postgresql.org/issues/2620>`_ - Set database name to blank('') when job type is set to batch, while creating pgAgent job
|
||||
| `Bug #2631 <https://redmine.postgresql.org/issues/2631>`_ - Change mapping of cell from 'numeric' to 'integer' for integer control as numeric cell has been removed from the code
|
||||
| `Bug #2633 <https://redmine.postgresql.org/issues/2633>`_ - Fix pgAgent job step issues
|
||||
| `Bug #2634 <https://redmine.postgresql.org/issues/2634>`_ - Add New Server through Quick links
|
||||
| `Bug #2637 <https://redmine.postgresql.org/issues/2637>`_ - Fix Copy so it still works after query results have been copied
|
||||
| `Bug #2641 <https://redmine.postgresql.org/issues/2641>`_ - User management issues - styling and inability to edit users properly
|
||||
| `Bug #2644 <https://redmine.postgresql.org/issues/2644>`_ - Fix alertify notification messages where checkmark box disconnected from frame
|
||||
| `Bug #2646 <https://redmine.postgresql.org/issues/2646>`_ - Fix the path reference of load-node.gif which was referencing to vendor directory
|
||||
| `Bug #2654 <https://redmine.postgresql.org/issues/2654>`_ - Update datetime picker
|
||||
| `Bug #2655 <https://redmine.postgresql.org/issues/2655>`_ - Fix connection string validation for pgAgent jobs
|
||||
| `Bug #2656 <https://redmine.postgresql.org/issues/2656>`_ - Change Datetimepicker to expand from bottom in pgAgent so calendar does not get hidden
|
||||
| `Bug #2657 <https://redmine.postgresql.org/issues/2657>`_ - Fix syntax error while saving changes for start/end time, weekdays, monthdays, month, hours, minutes while updating the pgAgent Job
|
||||
| `Bug #2659 <https://redmine.postgresql.org/issues/2659>`_ - Fix issue where unable to add/update variables for columns of a table
|
||||
| `Bug #2660 <https://redmine.postgresql.org/issues/2660>`_ - Not able to select rows in History Tab
|
||||
| `Bug #2668 <https://redmine.postgresql.org/issues/2668>`_ - Fix RE-SQL for triggers with a single arg
|
||||
| `Bug #2670 <https://redmine.postgresql.org/issues/2670>`_ - Improve datamodel validations for default Validator if user (developer) does not implement validate function in datamodel
|
||||
| `Bug #2671 <https://redmine.postgresql.org/issues/2671>`_ - Fix array data type formating for bigint, real, float, double precision
|
||||
| `Bug #2681 <https://redmine.postgresql.org/issues/2681>`_ - Reset query tool options before running tests
|
||||
| `Bug #2684 <https://redmine.postgresql.org/issues/2684>`_ - Fix layout of password prompt dialogue
|
||||
| `Bug #2691 <https://redmine.postgresql.org/issues/2691>`_ - View data option is missing from pgAdmin4 2.0 version
|
||||
| `Bug #2692 <https://redmine.postgresql.org/issues/2692>`_ - Base type is missing for Domain on pgAdmin4
|
||||
| `Bug #2693 <https://redmine.postgresql.org/issues/2693>`_ - User list is not available on User mapping pgAdmin4
|
||||
| `Bug #2698 <https://redmine.postgresql.org/issues/2698>`_ - User can not create function due to missing return type
|
||||
| `Bug #2699 <https://redmine.postgresql.org/issues/2699>`_ - Filtered Rows issue on pgAdmin4
|
||||
| `Bug #2700 <https://redmine.postgresql.org/issues/2700>`_ - Cancel button is visible after query executed succesfully
|
||||
| `Bug #2707 <https://redmine.postgresql.org/issues/2707>`_ - Disable trigger button does not work on pgAdmin4
|
||||
| `Bug #2708 <https://redmine.postgresql.org/issues/2708>`_ - Tablespace name should displayed instead of %s(new_tablespace)s with Move Objects to another tablespace
|
||||
| `Bug #2709 <https://redmine.postgresql.org/issues/2709>`_ - Display user relations in schema prefixed by 'pg'
|
||||
| `Bug #2713 <https://redmine.postgresql.org/issues/2713>`_ - Fix an exception seen sometimes when the server is restarted
|
||||
| `Bug #2742 <https://redmine.postgresql.org/issues/2742>`_ - Ensure using an alternate role to connect to a database doesn't cause an error when checking recovery state.
|
||||
@@ -46,7 +46,7 @@ little practical gain.
|
||||
Configuration
|
||||
*************
|
||||
|
||||
In order to configure pgAdmin to run in server mode, it is first necessary to
|
||||
In order to configure pgAdmin to run in server mode, it may be necessary to
|
||||
configure the Python code to run in multi-user mode, and then to configure the
|
||||
web server to find and execute the code.
|
||||
|
||||
@@ -68,19 +68,17 @@ pgAdmin. These are as follows:
|
||||
Python
|
||||
------
|
||||
|
||||
From pgAdmin 4 v2 onwards, server mode is the default configuration. If running under
|
||||
the desktop runtime, this is overridden automatically. There should typically be no
|
||||
need to modify the configuration simply to enable server mode to work, however it may
|
||||
be desirable to adjust some of the paths used.
|
||||
|
||||
In order to configure the Python code, follow these steps:
|
||||
|
||||
1. Create a ``config_local.py`` file alongside the existing ``config.py`` file.
|
||||
|
||||
2. Edit ``config_local.py`` and add the following setting:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SERVER_MODE = True
|
||||
|
||||
3. In most cases, the default file locations are setup for running in desktop mode.
|
||||
Add settings similar to the following to ``config_local.py`` to use paths suitable
|
||||
for server mode.
|
||||
2. Edit ``config_local.py`` and add the following settings. In most cases, the default
|
||||
file locations should be appropriate:
|
||||
|
||||
*NOTE: You must ensure the directories specified are writeable by
|
||||
the user that the web server processes will be running as, e.g. apache or www-data.*
|
||||
@@ -122,6 +120,16 @@ application may be configured similarly to the example below:
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
Now open the file ``C:\Program Files\pgAdmin4\web\pgAdmin4.wsgi`` with your favorite editor and add the code
|
||||
below which will activate Python virtual environment when Apache server runs.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
activate_this = 'C:\Program Files\pgAdmin4\venv\Scripts\activate_this.py'
|
||||
execfile(activate_this, dict(__file__=activate_this))
|
||||
|
||||
**Note:** The changes made in ``pgAdmin4.wsgi`` file will revert when pgAdmin4 is either upgraded or downgraded.
|
||||
|
||||
Apache HTTPD Configuration (Linux/Unix)
|
||||
---------------------------------------
|
||||
@@ -134,7 +142,7 @@ application may be configured similarly to the example below:
|
||||
<VirtualHost *>
|
||||
ServerName pgadmin.example.com
|
||||
|
||||
WSGIDaemonProcess pgadmin processes=1 threads=25
|
||||
WSGIDaemonProcess pgadmin processes=1 threads=25 python-home=/path/to/python/virtualenv
|
||||
WSGIScriptAlias / /opt/pgAdmin4/web/pgAdmin4.wsgi
|
||||
|
||||
<Directory /opt/pgAdmin4/web>
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
.. _server_dialog:
|
||||
|
||||
*****************
|
||||
The Server Dialog
|
||||
The Server Dialog
|
||||
*****************
|
||||
|
||||
Use the *Server* dialog to describe a connection to a server. Note: you must ensure the pg_hba.conf file of the server from which you are connecting allows connections from the host of the client.
|
||||
Use the *Server* dialog to describe a connection to a server. Note: you must ensure the pg_hba.conf file of the server
|
||||
from which you are connecting allows connections from the host of the client.
|
||||
|
||||
The *Server* dialog organizes the connection of a server through the following dialog tabs: *General*, and *Connection*.
|
||||
The *Server* dialog organizes the connection of a server through the following dialog tabs: *General*, and *Connection*.
|
||||
|
||||
.. image:: images/server_general.png
|
||||
|
||||
Use the fields in the *General* tab to identify the server:
|
||||
|
||||
* Use the *Name* field to add a descriptive name for the server; the name specified will be displayed in the *pgAdmin* tree control of the client.
|
||||
* Use the *Name* field to add a descriptive name for the server; the name specified will be displayed in the *pgAdmin*
|
||||
tree control of the client.
|
||||
* Use the drop-down list box in the *Server group* field to specify the *pgAdmin* tree control parent node for the server.
|
||||
* Uncheck the checkbox next to *Connect now?* to instruct pgAdmin not to attempt a connection upon completion of the dialog. The default enables connection.
|
||||
* Uncheck the checkbox next to *Connect now?* to instruct pgAdmin not to attempt a connection upon completion of the
|
||||
dialog. The default enables connection.
|
||||
* Provide a comment about the server in the *Comments* field.
|
||||
|
||||
Click the *Connection* tab to continue.
|
||||
@@ -23,18 +26,72 @@ Click the *Connection* tab to continue.
|
||||
|
||||
Use the fields in the *Connection* tab to configure a connection:
|
||||
|
||||
* Specify the IP address of the server host, or the fully qualified domain name in the *Host name/address* field. On Unix based systems, the address field may be left blank to use the default PostgreSQL Unix Domain Socket on the local machine, or may be set to an alternate path containing a PostgreSQL socket. If you enter a path, the path must begin with a "/".
|
||||
* Specify the IP address of the server host, or the fully qualified domain name in the *Host name/address* field. On
|
||||
Unix based systems, the address field may be left blank to use the default PostgreSQL Unix Domain Socket on the local
|
||||
machine, or may be set to an alternate path containing a PostgreSQL socket. If you enter a path, the path must begin
|
||||
with a "/".
|
||||
* Enter the listener port number of the server host in the *Port* field. The default is *5432*.
|
||||
* Use the *Maintenance database* field to specify the name of the initial database to which the client will connect. If you will be using pgAgent or adminpack objects, the pgAgent schema and adminpack objects should be installed on that database.
|
||||
* Use the *User name* field to specify the name of a role that will be used when authenticating with the server.
|
||||
* Use the *Password* field to provide a password that will be supplied when authenticating with the server.
|
||||
* Check the box next to *Save password* to instruct pgAdmin to save the password for future use.
|
||||
* Use the *Role* field to specify the name of a role that has privileges that will be conveyed to the client after authentication with the server. This selection allows you to connect as one role, and then assume the permissions of this specified role after the connection is established. Note that the connecting role must be a member of the role specified.
|
||||
* Use the drop-down list box in the SSL field to select the type of SSL connection the server should use. For more information about using SSL encryption, see Section 31.18 of the Postgres documentation:
|
||||
|
||||
http://www.postgresql.org/docs/9.5/static/libpq-ssl.html
|
||||
* Use the *Maintenance database* field to specify the name of the initial database to which the client will connect.
|
||||
If you will be using pgAgent or adminpack objects, the pgAgent schema and adminpack objects should be installed on that
|
||||
database.
|
||||
* Use the *User name* field to specify the name of a role that will be used when authenticating with the server.
|
||||
* Use the *Password* field to provide a password that will be supplied when authenticating with the server.
|
||||
* Check the box next to *Save password* to instruct pgAdmin to save the password for future use.
|
||||
* Use the *Role* field to specify the name of a role that has privileges that will be conveyed to the client after
|
||||
authentication with the server. This selection allows you to connect as one role, and then assume the permissions of
|
||||
this specified role after the connection is established. Note that the connecting role must be a member of the role
|
||||
specified.
|
||||
|
||||
Click the *SSL* tab to continue.
|
||||
|
||||
.. image:: images/server_ssl.png
|
||||
|
||||
Use the fields in the *SSL* tab to configure a SSL:
|
||||
|
||||
* Use the drop-down list box in the SSL field to select the type of SSL connection the server should use. For more
|
||||
information about using SSL encryption, see Section 31.18 of the Postgres documentation:
|
||||
|
||||
http://www.postgresql.org/docs/9.5/static/libpq-ssl.html
|
||||
|
||||
* Specify the file containing the name of the client SSL certificate, replacing the default *~/.postgresql/postgresql.crt*
|
||||
in case of Desktop mode and *<STORAGE_DIR>/<USERNAME>/.postgresql/postgresql.crt* in case of Web mode. This parameter
|
||||
is ignored if an SSL connection is not made.
|
||||
* Specify the file containing the secret key used for the client certificate, replacing the default *~/.postgresql/postgresql.key*
|
||||
in case of Desktop mode and *<STORAGE_DIR>/<USERNAME>/.postgresql/postgresql.key* in case of Web mode. This parameter
|
||||
is ignored if an SSL connection is not made.
|
||||
* Specify the file containing the SSL certificate authority, replacing the default *~/.postgresql/root.crt*. This
|
||||
parameter is ignored if an SSL connection is not made.
|
||||
* Specify the file containing the SSL certificate revocation list, replacing the default *~/.postgresql/root.crl*. This
|
||||
parameter is ignored if an SSL connection is not made.
|
||||
* If set to True, data sent over SSL connections will be compressed else compression will be disabled, The default
|
||||
is *False*. This parameter is ignored if an SSL connection is not made, see Section 32.1.2 of the Postgres documentation
|
||||
for more details:
|
||||
|
||||
In Server mode, files can be uploaded to the server using the File chooser dialog.
|
||||
|
||||
https://www.postgresql.org/docs/9.6/static/libpq-connect.html
|
||||
|
||||
*WARNING:* In Server mode, certificates, private keys and the revocation list are stored in the per-user file storage
|
||||
area on the server, which is owned by the user account under which the pgAdmin server process is run. This means that
|
||||
administrators of the server may be able to access those files, so appropriate caution should be taken before choosing
|
||||
to use this feature.
|
||||
|
||||
Click the *Advanced* tab to continue.
|
||||
|
||||
.. image:: images/server_advanced.png
|
||||
|
||||
Use the fields in the *Advanced* tab to configure a connection:
|
||||
|
||||
* Specify the IP address of the server host. Using this field to specify the host IP address will avoid a DNS lookup on
|
||||
connection, however it may be useful to specify both a host name and address when using Kerberos, GSSAPI, or SSPI
|
||||
authentication methods, as well as for verify-full SSL certificate verification
|
||||
* The DB restriction field allows you to enter an SQL restriction that will be used against the pg_database table to
|
||||
limit the databases that you see. For example, you might enter: *live_db test_db* so that only live_db and test_db are
|
||||
shown in the pgAdmin browser. Separate entries with a comma or tab as you type.
|
||||
* Specify the password file which allow user to login without providing password, see Section 31 of the Postgres documentation:
|
||||
|
||||
https://www.postgresql.org/docs/9.6/static/libpq-pgpass.html
|
||||
|
||||
* Click the *Save* button to save work.
|
||||
* Click the *Cancel* button to exit without saving work.
|
||||
* Click the *Reset* button to restore configuration parameters.
|
||||
|
||||
@@ -28,6 +28,17 @@ might run::
|
||||
|
||||
to create a patch between your development branch and the public master branch.
|
||||
|
||||
You can also create patches directly from the development tree, for example::
|
||||
|
||||
$ git diff > my_cool_feature.diff
|
||||
|
||||
If you are adding new files, you may need to stage them for commit, and then
|
||||
create your patch against the staging area. If any of the files are binary,
|
||||
for example, images, you will need to use the *--binary* option::
|
||||
|
||||
$ git add file1.py file2.py images/image1.png [...]
|
||||
$ git diff --cached --binary > my_cool_feature.diff
|
||||
|
||||
Once you have your patch, check it thoroughly to ensure it meets the pgAdmin
|
||||
:doc:`coding_standards`, and review it against the :doc:`code_review` to minimise
|
||||
the chances of it being rejected. Once you're happy with your work, mail it
|
||||
|
||||
@@ -2,37 +2,38 @@
|
||||
Translations
|
||||
************
|
||||
|
||||
pgAdmin supports multiple languages using the `Flask-Babel
|
||||
<https://pythonhosted.org/Flask-Babel/>`_ Python module. A list of supported
|
||||
languages is included in the **web/config.py** configuration file and must be
|
||||
updated whenever langauges are added or removed.
|
||||
pgAdmin supports multiple languages using the `Flask-Babel
|
||||
<https://pythonhosted.org/Flask-Babel/>`_ Python module. A list of supported
|
||||
languages is included in the **web/config.py** configuration file and must be
|
||||
updated whenever languages are added or removed with
|
||||
`ISO 639-1 <https://en.wikipedia.org/wiki/ISO_639-1>`_ (two letter) language
|
||||
codes. The codes are named **$LANG** in this document.
|
||||
|
||||
Translation Marking
|
||||
===================
|
||||
|
||||
Strings can be marked for translation in either Python code (using **gettext()**)
|
||||
or Jinja templates (using **_()**). Here are some examples that show how this
|
||||
or Jinja templates (using **_()**). Here are some examples that show how this
|
||||
is achieved.
|
||||
|
||||
Python::
|
||||
|
||||
errormsg = gettext('No server group name was specified')
|
||||
|
||||
|
||||
Jinja:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<input type="submit" value="{{ _('Change Password') }}">
|
||||
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<title>{{ _('%(appname)s Password Change', appname=config.APP_NAME) }}</title>
|
||||
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
define(['sources/gettext', ...], function(gettext, ...){
|
||||
...
|
||||
|
||||
var alert = alertify.prompt(
|
||||
gettext('Password Change'),
|
||||
gettext('New password for %(userName)s', {userName: 'jsmith' }),
|
||||
@@ -40,83 +41,34 @@ Jinja:
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
|
||||
Updating and Merging
|
||||
====================
|
||||
|
||||
Whenever new strings are added to the application, the template catalogues
|
||||
(**web/pgadmin/messages.pot**) must be updated and the existing catalogues
|
||||
merged with the updated template and compiled. This can be achieved using the
|
||||
following command from the **web** directory, in the Python virtual environment
|
||||
used for pgAdmin:
|
||||
Whenever new strings are added to the application, the template catalogue
|
||||
(**web/pgadmin/messages.pot**) and the existing translation
|
||||
catalogues (**web/pgadmin/translations/$LANG/LC_MESSAGES/messages.po**) must be updated
|
||||
and compiled. This can be achieved using the following commands from the
|
||||
**web** directory in the Python virtual environment for pgAdmin:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
(pgadmin4)piranha:web dpage$ pybabel extract -F babel.cfg -o pgadmin/messages.pot pgadmin
|
||||
|
||||
For example:
|
||||
(pgadmin4) user$ pybabel extract -F babel.cfg -o pgadmin/messages.pot pgadmin
|
||||
|
||||
Once the template has been updated it needs to be merged into the existing
|
||||
message catalogues:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
(pgadmin4)piranha:web dpage$ pybabel extract -F babel.cfg -o pgadmin/messages.pot pgadmin
|
||||
extracting messages from pgadmin/__init__.py
|
||||
extracting messages from pgadmin/about/__init__.py
|
||||
extracting messages from pgadmin/about/hooks.py
|
||||
extracting messages from pgadmin/about/views.py
|
||||
extracting messages from pgadmin/about/templates/about/index.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/browser/__init__.py
|
||||
extracting messages from pgadmin/browser/hooks.py
|
||||
extracting messages from pgadmin/browser/views.py
|
||||
extracting messages from pgadmin/browser/nodes/CollectionNode.py
|
||||
extracting messages from pgadmin/browser/nodes/ObjectNode.py
|
||||
extracting messages from pgadmin/browser/nodes/__init__.py
|
||||
extracting messages from pgadmin/browser/nodes/server_groups/__init__.py
|
||||
extracting messages from pgadmin/browser/nodes/server_groups/hooks.py
|
||||
extracting messages from pgadmin/browser/nodes/server_groups/views.py
|
||||
extracting messages from pgadmin/browser/templates/browser/body.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/browser/templates/browser/index.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/browser/templates/browser/messages.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/help/__init__.py
|
||||
extracting messages from pgadmin/help/hooks.py
|
||||
extracting messages from pgadmin/help/views.py
|
||||
extracting messages from pgadmin/redirects/__init__.py
|
||||
extracting messages from pgadmin/redirects/views.py
|
||||
extracting messages from pgadmin/settings/__init__.py
|
||||
extracting messages from pgadmin/settings/hooks.py
|
||||
extracting messages from pgadmin/settings/settings_model.py
|
||||
extracting messages from pgadmin/settings/views.py
|
||||
extracting messages from pgadmin/templates/base.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/templates/security/change_password.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/templates/security/fields.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/templates/security/forgot_password.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/templates/security/login_user.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/templates/security/messages.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/templates/security/panel.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/templates/security/reset_password.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/templates/security/watermark.html (extensions="jinja2.ext.autoescape,jinja2.ext.with_")
|
||||
extracting messages from pgadmin/test/__init__.py
|
||||
extracting messages from pgadmin/test/hooks.py
|
||||
extracting messages from pgadmin/test/views.py
|
||||
extracting messages from pgadmin/utils/__init__.py
|
||||
extracting messages from pgadmin/utils/views.py
|
||||
writing PO template file to pgadmin/messages.pot
|
||||
|
||||
Once the template has been updated, it needs to be merged into the existing
|
||||
message catalogues, for example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
(pgadmin4)piranha:web dpage$ pybabel update -i pgadmin/messages.pot -d pgadmin/translations
|
||||
updating catalog 'pgadmin/translations/fr/LC_MESSAGES/messages.po' based on 'pgadmin/messages.pot'
|
||||
(pgadmin4) user$ pybabel update -i pgadmin/messages.pot -d pgadmin/translations
|
||||
|
||||
Finally, the message catalogues can be compiled for use:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
(pgadmin4)piranha:web dpage$ pybabel compile -d pgadmin/translations
|
||||
compiling catalog 'pgadmin/translations/fr/LC_MESSAGES/messages.po' to 'pgadmin/translations/fr/LC_MESSAGES/messages.mo'
|
||||
(pgadmin4) user$ pybabel compile -d pgadmin/translations
|
||||
|
||||
Adding a new Language
|
||||
Adding a New Language
|
||||
=====================
|
||||
|
||||
Adding a new language is simple. First, add the language name and identifier to
|
||||
@@ -125,15 +77,14 @@ Adding a new language is simple. First, add the language name and identifier to
|
||||
# Languages we support in the UI
|
||||
LANGUAGES = {
|
||||
'en': 'English',
|
||||
'fr': 'Français'
|
||||
'zh': 'Chinese (Simplified)',
|
||||
'de': 'German',
|
||||
'pl': 'Polish'
|
||||
}
|
||||
|
||||
Then, create the new message catalogue from the **web** directory in the source
|
||||
tree, in the Python virtual environment used for pgAdmin:
|
||||
Then, create the new message catalogue from the **web** directory in the source
|
||||
tree in the Python virtual environment for pgAdmin:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
(pgadmin4)piranha:web dpage$ pybabel init -i pgadmin/messages.pot -d pgadmin/translations -l fr
|
||||
|
||||
This will initialise a new catalogue for a French translation.
|
||||
|
||||
(pgadmin4) user$ pybabel init -i pgadmin/messages.pot -d pgadmin/translations -l $LANG
|
||||
@@ -1,36 +1,38 @@
|
||||
This is a list of the third party libraries/code used in the application, not
|
||||
including Python modules installed via Pip which are listed in requirements.txt.
|
||||
|
||||
Library Version Licence URL
|
||||
======= ======= ======= ===
|
||||
QT 4.6.2+ LGPL v2.1/3 http://www.qt.io/
|
||||
Bootstrap 3.3.4 MIT http://getbootstrap.com/
|
||||
jQuery 1.11.1 MIT http://jquery.com/
|
||||
Modernizr 2.6.2 MIT/BSD http://modernizr.com/
|
||||
AlertifyJS 1.7.1 MIT http://alertifyjs.com/ *** Do not update - changed to GPL3 in 1.8.0 ***
|
||||
CodeMirror 5.14.2 MIT http://codemirror.net/
|
||||
aciTree 4.5.0-rc.7 MIT/GPL http://acoderinsights.ro/en/aciTree-tree-view-with-jQuery
|
||||
contextMenu 2.1.0 MIT https://github.com/swisnl/jQuery-contextMenu
|
||||
wcDocker 0f5690318c MIT/GPL https://github.com/WebCabin/wcDocker
|
||||
Require.js 2.1.18 BSD/MIT http://requirejs.org/
|
||||
Underscore.js 1.8.3 MIT http://underscorejs.org/
|
||||
Underscore.string 387ab72d49 MIT http://epeli.github.io/underscore.string/
|
||||
Backform.js 5859b4f9db MIT https://github.com/AmiliaApp/backform
|
||||
Backbone 1.1.2 MIT http://backbonejs.org
|
||||
font-Awesome 4.5 SIL OFL http://fortawesome.github.io/Font-Awesome/
|
||||
font-mfizz 2.3 MIT http://fizzed.com/oss/font-mfizz
|
||||
backgrid.js 0.3.5 MIT http://backgridjs.com/
|
||||
backbone.undo 0.2 MIT http://backbone.undojs.com/
|
||||
bootstrap-switch 3.3.2 MIT http://www.bootstrap-switch.org/
|
||||
select2 4.0.1 MIT https://select2.github.io/
|
||||
backgrid-filter 01b2b21 MIT https://github.com/wyuenho/backgrid-filter
|
||||
backbone.paginator 2.0.3 MIT http://github.com/backbone-paginator/backbone.paginator
|
||||
backgrid-paginator 03632df MIT https://github.com/wyuenho/backgrid-paginator
|
||||
backgrid-select-all 1a00053 MIT https://github.com/wyuenho/backgrid-select-all
|
||||
dropzone 4e20bd4 MIT https://github.com/enyo/dropzone
|
||||
Filemanager 7e060c2 MIT https://github.com/simogeo/Filemanager
|
||||
Unit (Length) d8e6237 MIT https://github.com/heygrady/Units/blob/master/Length.min.js
|
||||
Natural Sort 9565816 MIT https://github.com/javve/natural-sort/blob/master/index.js
|
||||
SlickGrid 2.2.4 MIT https://github.com/6pac/SlickGrid
|
||||
jQuery-UI 1.11.3 MIT https://jqueryui.com/
|
||||
BigNumber 3.0.1 MIT http://mikemcl.github.io/bignumber.js
|
||||
Library Version Licence URL
|
||||
======= ======= ======= ===
|
||||
QT 4.6.2+ LGPL v2.1/3 http://www.qt.io/
|
||||
Bootstrap 3.3.4 MIT http://getbootstrap.com/
|
||||
jQuery 1.11.1 MIT http://jquery.com/
|
||||
Modernizr 2.6.2 MIT/BSD http://modernizr.com/
|
||||
AlertifyJS 1.7.1 MIT http://alertifyjs.com/ *** Do not update - changed to GPL3 in 1.8.0 ***
|
||||
CodeMirror 5.14.2 MIT http://codemirror.net/
|
||||
aciTree 4.5.0-rc.7 MIT/GPL http://acoderinsights.ro/en/aciTree-tree-view-with-jQuery
|
||||
contextMenu 2.1.0 MIT https://github.com/swisnl/jQuery-contextMenu
|
||||
wcDocker 0f5690318c MIT/GPL https://github.com/WebCabin/wcDocker
|
||||
Require.js 2.1.18 BSD/MIT http://requirejs.org/
|
||||
Underscore.js 1.8.3 MIT http://underscorejs.org/
|
||||
Underscore.string 387ab72d49 MIT http://epeli.github.io/underscore.string/
|
||||
Backform.js 5859b4f9db MIT https://github.com/AmiliaApp/backform
|
||||
Backbone 1.1.2 MIT http://backbonejs.org
|
||||
font-Awesome 4.5 SIL OFL http://fortawesome.github.io/Font-Awesome/
|
||||
font-mfizz 2.4.1 MIT http://fizzed.com/oss/font-mfizz
|
||||
backgrid.js 0.3.5 MIT http://backgridjs.com/
|
||||
backbone.undo 0.2 MIT http://backbone.undojs.com/
|
||||
bootstrap-switch 3.3.2 MIT http://www.bootstrap-switch.org/
|
||||
select2 4.0.1 MIT https://select2.github.io/
|
||||
backgrid-filter 01b2b21 MIT https://github.com/wyuenho/backgrid-filter
|
||||
backbone.paginator 2.0.3 MIT http://github.com/backbone-paginator/backbone.paginator
|
||||
backgrid-paginator 03632df MIT https://github.com/wyuenho/backgrid-paginator
|
||||
backgrid-select-all 1a00053 MIT https://github.com/wyuenho/backgrid-select-all
|
||||
dropzone 4e20bd4 MIT https://github.com/enyo/dropzone
|
||||
Filemanager 7e060c2 MIT https://github.com/simogeo/Filemanager
|
||||
Unit (Length) d8e6237 MIT https://github.com/heygrady/Units/blob/master/Length.min.js
|
||||
Natural Sort 9565816 MIT https://github.com/javve/natural-sort/blob/master/index.js
|
||||
SlickGrid 2.3.7-8ff6814 MIT https://github.com/6pac/SlickGrid
|
||||
jQuery-UI 1.11.3 MIT https://jqueryui.com/
|
||||
BigNumber 3.0.1 MIT http://mikemcl.github.io/bignumber.js
|
||||
Source Code Pro 1.1 SIL OFL https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700
|
||||
Open Sans 2.0 AL https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,700
|
||||
@@ -0,0 +1,48 @@
|
||||
########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
#########################################################################
|
||||
|
||||
# Get the basics out of the way
|
||||
FROM centos:latest
|
||||
|
||||
LABEL name="pgAdmin 4" \
|
||||
vendor="The pgAdmin Development Team" \
|
||||
license="PostgreSQL"
|
||||
|
||||
# We only need the web/ directory, and a few other things
|
||||
COPY web /var/www/pgadmin
|
||||
COPY requirements.txt /var/www/pgadmin
|
||||
|
||||
# Install everything we need. Use easy_install to get pip, to avoid setting up EPEL
|
||||
RUN yum install -y python-setuptools python-devel httpd mod_wsgi gcc
|
||||
RUN easy_install pip
|
||||
|
||||
# Now install the Python runtime dependencies
|
||||
RUN pip install -r /var/www/pgadmin/requirements.txt
|
||||
|
||||
# Create required directories for running
|
||||
RUN mkdir -p /var/log/pgadmin
|
||||
RUN chown -R apache /var/log/pgadmin
|
||||
RUN mkdir -p /var/lib/pgadmin
|
||||
RUN chown -R apache /var/lib/pgadmin
|
||||
|
||||
# Apache config time
|
||||
COPY pgadmin4.conf /etc/httpd/conf.d/
|
||||
COPY entry.sh /
|
||||
|
||||
# Finally, remove packages we only needed for building
|
||||
RUN yum -y remove gcc cpp glibc-devel glibc-headers kernel-headers libgomp libmpc mpfr
|
||||
|
||||
# Default config options
|
||||
ENV PGADMIN_DEFAULT_EMAIL container@pgadmin.org
|
||||
ENV PGADMIN_DEFAULT_PASSWORD Conta1ner
|
||||
|
||||
EXPOSE 80 443
|
||||
|
||||
# Start the service
|
||||
ENTRYPOINT ["/bin/bash", "/entry.sh"]
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/bin/bash
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
#########################################################################
|
||||
|
||||
# Runtime checks
|
||||
if [ ! -d runtime -a ! -d web ]; then
|
||||
echo This script must be run from the top-level directory of the source tree.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d .git -a ! -f .git/config ]; then
|
||||
echo This script must be run from a git checkout of the source tree.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the required package info
|
||||
APP_RELEASE=`grep "^APP_RELEASE" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
|
||||
APP_REVISION=`grep "^APP_REVISION" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
|
||||
APP_NAME=`grep "^APP_NAME" web/config.py | cut -d"=" -f2 | sed "s/'//g" | sed 's/^ //'`
|
||||
APP_LONG_VERSION=$APP_RELEASE.$APP_REVISION
|
||||
APP_SHORT_VERSION=`echo $APP_LONG_VERSION | cut -d . -f1,2`
|
||||
APP_SUFFIX=`grep "^APP_SUFFIX" web/config.py | cut -d"=" -f2 | sed 's/ //g' | sed "s/'//g"`
|
||||
if [ ! -z $APP_SUFFIX ]; then
|
||||
export APP_LONG_VERSION=$APP_LONG_VERSION-$APP_SUFFIX
|
||||
fi
|
||||
CONTAINER_NAME=`echo $APP_NAME | sed 's/ //g' | awk '{print tolower($0)}'`
|
||||
|
||||
# Output basic details to show we're working
|
||||
echo Building Docker Container for $APP_NAME version $APP_LONG_VERSION...
|
||||
|
||||
# Create/clearout the build directory
|
||||
echo Creating/cleaning required directories...
|
||||
if [ -d docker-build ]; then
|
||||
rm -rf docker-build
|
||||
fi
|
||||
|
||||
mkdir docker-build
|
||||
|
||||
# Create the output directory if not present
|
||||
if [ ! -d dist ]; then
|
||||
mkdir dist
|
||||
fi
|
||||
|
||||
# Build the clean tree
|
||||
for FILE in `git ls-files web`
|
||||
do
|
||||
echo Adding $FILE
|
||||
# We use tar here to preserve the path, as Mac (for example) doesn't support cp --parents
|
||||
tar cf - $FILE | (cd docker-build; tar xf -)
|
||||
done
|
||||
|
||||
pushd web
|
||||
yarn install
|
||||
yarn run bundle
|
||||
|
||||
for FILE in `ls -d pgAdmin/static/js/generated/*`
|
||||
do
|
||||
echo Adding $FILE
|
||||
tar cf - $FILE | (cd ../docker-build/web; tar xf -)
|
||||
done
|
||||
popd
|
||||
|
||||
# Build the docs
|
||||
if [ -d docs/en_US/_build/html ]; then
|
||||
rm -rf docs/en_US/_build/html
|
||||
fi
|
||||
|
||||
LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 make -C docs/en_US -f Makefile.sphinx html
|
||||
|
||||
mkdir docker-build/web/docs
|
||||
cp -R docs/en_US/_build/html/* docker-build/web/docs/
|
||||
|
||||
# Configure pgAdmin
|
||||
echo "HELP_PATH = '../../docs/'" >> docker-build/web/config_distro.py
|
||||
echo "DEFAULT_BINARY_PATHS = {" >> docker-build/web/config_distro.py
|
||||
echo " 'pg': ''," >> docker-build/web/config_distro.py
|
||||
echo " 'ppas': ''," >> docker-build/web/config_distro.py
|
||||
echo " 'gpdb': ''" >> docker-build/web/config_distro.py
|
||||
echo "}" >> docker-build/web/config_distro.py
|
||||
|
||||
# Copy the Docker specific assets into place
|
||||
cp pkg/docker/Dockerfile docker-build/
|
||||
cp pkg/docker/entry.sh docker-build/
|
||||
cp pkg/docker/pgadmin4.conf docker-build/
|
||||
cp requirements.txt docker-build/
|
||||
|
||||
# Build the container
|
||||
docker build docker-build -t $CONTAINER_NAME \
|
||||
-t $CONTAINER_NAME:latest \
|
||||
-t $CONTAINER_NAME:$APP_RELEASE \
|
||||
-t $CONTAINER_NAME:$APP_LONG_VERSION
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
#########################################################################
|
||||
|
||||
export PGADMIN_SETUP_EMAIL=${PGADMIN_DEFAULT_EMAIL}
|
||||
export PGADMIN_SETUP_PASSWORD=${PGADMIN_DEFAULT_PASSWORD}
|
||||
|
||||
/usr/sbin/httpd -D FOREGROUND
|
||||
@@ -0,0 +1,22 @@
|
||||
########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
#########################################################################
|
||||
|
||||
ServerName pgadmin4
|
||||
|
||||
<VirtualHost *>
|
||||
WSGIDaemonProcess pgadmin processes=1 threads=25
|
||||
WSGIScriptAlias / /var/www/pgadmin/pgAdmin4.wsgi
|
||||
|
||||
<Directory /var/www/pgadmin>
|
||||
WSGIProcessGroup pgadmin
|
||||
WSGIApplicationGroup %{GLOBAL}
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
@@ -87,7 +87,7 @@ _create_python_virtualenv() {
|
||||
cd $BUILDROOT
|
||||
test -d $VIRTUALENV || virtualenv -p $PYTHON $VIRTUALENV || exit 1
|
||||
source $VIRTUALENV/bin/activate
|
||||
$PIP install -r $SOURCEDIR/requirements.txt || { echo PIP install failed. Please resolve the issue and rerun the script; exit 1; }
|
||||
$PIP install --no-cache-dir --no-binary :all: -r $SOURCEDIR/requirements.txt || { echo PIP install failed. Please resolve the issue and rerun the script; exit 1; }
|
||||
|
||||
# Figure out some paths for use when completing the venv
|
||||
# Use "python" here as we want the venv path
|
||||
@@ -128,6 +128,9 @@ _create_python_virtualenv() {
|
||||
if test -d $DIR_PYMODULES_PATH; then
|
||||
mv $DIR_PYMODULES_PATH $DIR_PYMODULES_PATH/../python
|
||||
fi
|
||||
|
||||
# Fix the backports module which will have no __init__.py file
|
||||
touch $BUILDROOT/$VIRTUALENV/lib/python/site-packages/backports/__init__.py
|
||||
}
|
||||
|
||||
_build_runtime() {
|
||||
@@ -175,6 +178,11 @@ _complete_bundle() {
|
||||
# run complete-bundle to copy the dependent libraries and frameworks and fix the rpaths
|
||||
./complete-bundle.sh "$BUILDROOT/$APP_BUNDLE_NAME" || { echo complete-bundle.sh failed; exit 1; }
|
||||
|
||||
pushd $SOURCEDIR/web
|
||||
yarn install
|
||||
yarn run bundle
|
||||
popd
|
||||
|
||||
# copy the web directory to the bundle as it is required by runtime
|
||||
cp -r $SOURCEDIR/web "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/" || exit 1
|
||||
cd "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/web"
|
||||
|
||||
@@ -57,6 +57,15 @@ do
|
||||
tar cf - $FILE | (cd ../pip-build/pgadmin4; tar xf -)
|
||||
done
|
||||
|
||||
yarn install
|
||||
yarn run bundle
|
||||
|
||||
for FILE in `ls -d pgAdmin/static/js/generated/*`
|
||||
do
|
||||
echo Adding $FILE
|
||||
tar cf - $FILE | (cd ../pip-build/pgadmin4; tar xf -)
|
||||
done
|
||||
|
||||
cd ../docs
|
||||
for FILE in `git ls-files`
|
||||
do
|
||||
|
||||
@@ -13,6 +13,14 @@ import imp
|
||||
from setuptools import setup
|
||||
from codecs import open
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
import builtins
|
||||
else:
|
||||
import __builtin__ as builtins
|
||||
|
||||
# Ensure the global server mode is set.
|
||||
builtins.SERVER_MODE = None
|
||||
|
||||
"""This script helps to generate PIP packages"""
|
||||
|
||||
# Get the requirements list for the current version of Python
|
||||
@@ -79,7 +87,7 @@ setup(
|
||||
extras_require={
|
||||
# ...
|
||||
":python_version<'2.7'": ["ordereddict"],
|
||||
":python_version<='2.7'": ["importlib"],
|
||||
":python_version<='2.7'": ["backports.csv", "importlib"],
|
||||
},
|
||||
|
||||
# Specify data files to be included. For Python 2.6 include them in MANIFEST.in
|
||||
|
||||
@@ -69,6 +69,17 @@ do
|
||||
tar cf - $FILE | (cd src-build/$TARBALL_NAME; tar xf -)
|
||||
done
|
||||
|
||||
pushd web
|
||||
yarn install
|
||||
yarn run bundle
|
||||
|
||||
for FILE in `ls -d pgAdmin/static/js/generated/*`
|
||||
do
|
||||
echo Adding $FILE
|
||||
tar cf - $FILE | (cd ../src-build/$TARBALL_NAME/web; tar xf -)
|
||||
done
|
||||
popd
|
||||
|
||||
# Create the tarball
|
||||
echo Creating tarball...
|
||||
cd src-build
|
||||
|
||||
@@ -13,6 +13,7 @@ Flask-Gravatar==0.4.2
|
||||
Flask-HTMLmin==1.2
|
||||
Flask-Login==0.3.2
|
||||
Flask-Mail==0.9.1
|
||||
Flask-Migrate==2.0.3
|
||||
Flask-Principal==0.4.0
|
||||
Flask-Security==1.7.5
|
||||
Flask-SQLAlchemy==2.1
|
||||
@@ -26,7 +27,7 @@ MarkupSafe==0.23
|
||||
ordereddict; python_version < '2.7'
|
||||
passlib==1.6.2
|
||||
pbr==1.9.1
|
||||
psycopg2==2.6.2
|
||||
psycopg2>=2.7.1
|
||||
pycrypto==2.6.1
|
||||
pyrsistent==0.11.13
|
||||
python-dateutil==2.5.0
|
||||
@@ -39,3 +40,6 @@ SQLAlchemy==1.0.14
|
||||
sqlparse==0.1.19
|
||||
Werkzeug==0.9.6
|
||||
WTForms==2.0.2
|
||||
backports.csv==1.0.4; python_version <= '2.7'
|
||||
Flask-Paranoid==0.1.0
|
||||
Flask-Script==2.0.5; python_version < '2.7'
|
||||
@@ -66,15 +66,26 @@ BrowserWindow::BrowserWindow(QString url)
|
||||
// Setup the shortcuts
|
||||
createActions();
|
||||
|
||||
m_tabWidget = new TabWindow(this);
|
||||
m_tabWidget = new DockTabWidget(this);
|
||||
m_tabWidget->tabBar()->setVisible(false);
|
||||
|
||||
m_mainGridLayout = new QGridLayout(m_tabWidget);
|
||||
m_mainGridLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_pgAdminMainTab = new QWidget(m_tabWidget);
|
||||
m_tabGridLayout = new QGridLayout(m_pgAdminMainTab);
|
||||
m_tabGridLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_mainWebView = new WebViewWindow(m_pgAdminMainTab);
|
||||
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
m_mainWebView->setPage(new WebEnginePage());
|
||||
#else
|
||||
m_cookieJar = new QNetworkCookieJar();
|
||||
m_netAccessMan = new QNetworkAccessManager();
|
||||
m_netAccessMan->setCookieJar(m_cookieJar);
|
||||
m_mainWebView->setPage(new WebViewPage());
|
||||
m_mainWebView->page()->setNetworkAccessManager(m_netAccessMan);
|
||||
m_mainWebView->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
|
||||
m_mainWebView->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
|
||||
#endif
|
||||
|
||||
#ifdef PGADMIN4_DEBUG
|
||||
@@ -107,10 +118,12 @@ BrowserWindow::BrowserWindow(QString url)
|
||||
#else
|
||||
// Register the slot when click on the URL link form main menu bar
|
||||
connect(m_mainWebView, SIGNAL(linkClicked(const QUrl &)),SLOT(urlLinkClicked(const QUrl &)));
|
||||
// Register the slot when click on the URL link for QWebPage
|
||||
connect(m_mainWebView->page(), SIGNAL(createTabWindowKit(QWebPage * &)),SLOT(createNewTabWindowKit(QWebPage * &)));
|
||||
#endif
|
||||
|
||||
// Register the slot on tab index change
|
||||
connect(m_tabWidget,SIGNAL(currentChanged(int )),this,SLOT(tabIndexChanged(int )));
|
||||
connect(m_tabWidget,SIGNAL(currentChanged(int )), m_tabWidget,SLOT(tabIndexChanged(int )));
|
||||
|
||||
// Listen for download file request from the web page
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
@@ -120,6 +133,7 @@ BrowserWindow::BrowserWindow(QString url)
|
||||
m_mainWebView->page()->setForwardUnsupportedContent(true);
|
||||
connect(m_mainWebView->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(download(const QNetworkRequest &)));
|
||||
connect(m_mainWebView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*)));
|
||||
m_mainWebView->page()->setForwardUnsupportedContent(true);
|
||||
m_mainWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
|
||||
#endif
|
||||
|
||||
@@ -150,6 +164,13 @@ BrowserWindow::BrowserWindow(QString url)
|
||||
m_mainWebView->setUrl(m_appServerUrl);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
BrowserWindow::~BrowserWindow()
|
||||
{
|
||||
if (m_tabWidget)
|
||||
delete m_tabWidget;
|
||||
}
|
||||
|
||||
// Save the window geometry on close
|
||||
void BrowserWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
@@ -178,6 +199,8 @@ void BrowserWindow::createActions()
|
||||
exitShortcut->setContext(Qt::ApplicationShortcut);
|
||||
connect(exitShortcut, SIGNAL(activated()), this, SLOT(close()));
|
||||
|
||||
signalMapper = new QSignalMapper(this);
|
||||
|
||||
// About box
|
||||
aboutShortcut = new QShortcut(QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_A), this);
|
||||
aboutShortcut->setContext(Qt::ApplicationShortcut);
|
||||
@@ -186,12 +209,23 @@ void BrowserWindow::createActions()
|
||||
// Zoom in
|
||||
zoomInShortcut = new QShortcut(QKeySequence(QKeySequence::ZoomIn), this);
|
||||
zoomInShortcut->setContext(Qt::ApplicationShortcut);
|
||||
connect(zoomInShortcut, SIGNAL(activated()), this, SLOT(zoomIn()));
|
||||
signalMapper->setMapping(zoomInShortcut, 1);
|
||||
connect(zoomInShortcut, SIGNAL(activated()), signalMapper, SLOT(map()));
|
||||
|
||||
// Zoom out
|
||||
zoomOutShortcut = new QShortcut(QKeySequence(QKeySequence::ZoomOut), this);
|
||||
zoomOutShortcut->setContext(Qt::ApplicationShortcut);
|
||||
connect(zoomOutShortcut, SIGNAL(activated()), this, SLOT(zoomOut()));
|
||||
signalMapper->setMapping(zoomOutShortcut, -1);
|
||||
connect(zoomOutShortcut, SIGNAL(activated()), signalMapper, SLOT(map()));
|
||||
|
||||
// Reset Zoom
|
||||
zoomResetShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_0), this);
|
||||
zoomResetShortcut->setContext(Qt::ApplicationShortcut);
|
||||
signalMapper->setMapping(zoomResetShortcut, 0);
|
||||
connect(zoomResetShortcut, SIGNAL(activated()), signalMapper, SLOT(map()));
|
||||
|
||||
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setZoomLevel(int)));
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
@@ -764,125 +798,14 @@ void BrowserWindow::unsupportedContent(QNetworkReply * reply)
|
||||
}
|
||||
}
|
||||
|
||||
// Slot: When the tab index change, hide/show the toolbutton displayed on tab
|
||||
void BrowserWindow::tabIndexChanged(int index)
|
||||
{
|
||||
int tabCount = 1;
|
||||
for (tabCount = 1; tabCount < m_tabWidget->count(); tabCount++)
|
||||
{
|
||||
if (tabCount != index)
|
||||
m_tabWidget->showHideToolButton(tabCount, 0);
|
||||
else
|
||||
m_tabWidget->showHideToolButton(tabCount, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the tab and remove the memory of the given index tab
|
||||
void BrowserWindow::closetabs()
|
||||
{
|
||||
int loopCount = 0;
|
||||
int index = 0;
|
||||
QPushButton *btn = NULL;
|
||||
int totalTabs = m_tabWidget->count();
|
||||
|
||||
// If QTabWidget contains only one tab then hide the TabBar window
|
||||
if ((totalTabs - 1) < 2)
|
||||
m_tabWidget->tabBar()->setVisible(false);
|
||||
else
|
||||
m_tabWidget->tabBar()->setVisible(true);
|
||||
|
||||
QObject *senderPtr = QObject::sender();
|
||||
if (senderPtr != NULL)
|
||||
{
|
||||
btn = dynamic_cast<QPushButton*>(senderPtr);
|
||||
index = m_tabWidget->getButtonIndex(btn);
|
||||
}
|
||||
|
||||
if (index != 0)
|
||||
{
|
||||
QWidget *tab = m_tabWidget->widget(index);
|
||||
WebViewWindow *webviewPtr = NULL;
|
||||
loopCount = 0;
|
||||
|
||||
// free the allocated memory when the tab is closed
|
||||
if (tab != NULL)
|
||||
delete tab;
|
||||
|
||||
// Adjust the tab index value if the tab is closed in between
|
||||
for (loopCount = 1; loopCount < totalTabs; loopCount++)
|
||||
{
|
||||
if (index > loopCount)
|
||||
continue;
|
||||
|
||||
QWidget *tab = m_tabWidget->widget(loopCount);
|
||||
if (tab != NULL)
|
||||
{
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
if (webviewPtr != NULL)
|
||||
webviewPtr->setTabIndex((webviewPtr->getTabIndex() - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slot: go back to page and enable/disable toolbutton
|
||||
void BrowserWindow::goBackPage()
|
||||
{
|
||||
WebViewWindow *webviewPtr = NULL;
|
||||
|
||||
QWidget *tab = m_tabWidget->widget(m_tabWidget->currentIndex());
|
||||
if (tab != NULL)
|
||||
{
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
if (webviewPtr != NULL)
|
||||
{
|
||||
webviewPtr->back();
|
||||
m_tabWidget->enableDisableToolButton(m_tabWidget->currentIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slot: go forward to page and enable/disable toolbutton
|
||||
void BrowserWindow::goForwardPage()
|
||||
{
|
||||
WebViewWindow *webviewPtr = NULL;
|
||||
|
||||
QWidget *tab = m_tabWidget->widget(m_tabWidget->currentIndex());
|
||||
if (tab != NULL)
|
||||
{
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
if (webviewPtr != NULL)
|
||||
{
|
||||
webviewPtr->forward();
|
||||
m_tabWidget->enableDisableToolButton(m_tabWidget->currentIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slot: set the title of tab when the new tab created or existing tab contents changed
|
||||
void BrowserWindow::tabTitleChanged(const QString &str)
|
||||
{
|
||||
WebViewWindow *nextWebViewPtr = NULL;
|
||||
bool flagTabText = false;
|
||||
QToolButton *backToolButton = NULL;
|
||||
QToolButton *forwardToolButton = NULL;
|
||||
|
||||
if (!str.isEmpty())
|
||||
{
|
||||
QObject *senderPtr = QObject::sender();
|
||||
@@ -892,19 +815,96 @@ void BrowserWindow::tabTitleChanged(const QString &str)
|
||||
webViewPtr = dynamic_cast<WebViewWindow*>(senderPtr);
|
||||
if (webViewPtr != NULL)
|
||||
{
|
||||
m_tabWidget->setTabText(webViewPtr->getTabIndex(), str);
|
||||
m_tabWidget->setTabToolTipText(webViewPtr->getTabIndex(), str);
|
||||
m_tabWidget->enableDisableToolButton(webViewPtr->getTabIndex());
|
||||
DockTabWidget *dock_tab_widget = dynamic_cast<DockTabWidget*>(webViewPtr->parent()->parent()->parent());
|
||||
if (dock_tab_widget != NULL)
|
||||
{
|
||||
for (int loopCount = dock_tab_widget->count();loopCount >= 0;loopCount--)
|
||||
{
|
||||
QWidget *tab = dock_tab_widget->widget(loopCount);
|
||||
if (tab != NULL)
|
||||
{
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
nextWebViewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
|
||||
if (nextWebViewPtr != NULL && nextWebViewPtr == webViewPtr)
|
||||
{
|
||||
// If tab title is for Query tool then we should hide tool buttons.
|
||||
QWidget *tab = dock_tab_widget->tabBar()->tabButton(loopCount, QTabBar::LeftSide);
|
||||
if (tab != NULL)
|
||||
{
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
QToolButton *toolBtnPtr = dynamic_cast<QToolButton*>(widgetPtr);
|
||||
if (toolBtnPtr != NULL)
|
||||
{
|
||||
if (!QString::compare(toolBtnPtr->toolTip(), tr("Go back"), Qt::CaseInsensitive))
|
||||
backToolButton = toolBtnPtr;
|
||||
if (!QString::compare(toolBtnPtr->toolTip(), tr("Go forward"), Qt::CaseInsensitive))
|
||||
forwardToolButton = toolBtnPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (backToolButton != NULL && forwardToolButton != NULL)
|
||||
{
|
||||
if (!str.startsWith("Query -"))
|
||||
{
|
||||
if (str.startsWith("Debugger"))
|
||||
{
|
||||
backToolButton->hide();
|
||||
forwardToolButton->hide();
|
||||
webViewPtr->setBackForwardButtonHidden(true);
|
||||
}
|
||||
// If user open any file in query tool then "Query -" name will not appear
|
||||
// but it is still query tool so hide the tool button.
|
||||
else if (!webViewPtr->getBackForwardButtonHidden())
|
||||
{
|
||||
backToolButton->show();
|
||||
forwardToolButton->show();
|
||||
webViewPtr->setBackForwardButtonHidden(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
backToolButton->hide();
|
||||
forwardToolButton->hide();
|
||||
webViewPtr->setBackForwardButtonHidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
dock_tab_widget->setTabText(loopCount, str);
|
||||
dock_tab_widget->setTabToolTipText(loopCount, str);
|
||||
dock_tab_widget->enableDisableToolButton(loopCount);
|
||||
flagTabText = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flagTabText)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!flagTabText)
|
||||
{
|
||||
dock_tab_widget->setTabText(dock_tab_widget->currentIndex(), str);
|
||||
dock_tab_widget->setTabToolTipText(dock_tab_widget->currentIndex(), str);
|
||||
dock_tab_widget->enableDisableToolButton(dock_tab_widget->currentIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tabWidget->setTabText(m_tabWidget->currentIndex(), str);
|
||||
m_tabWidget->setTabToolTipText(m_tabWidget->currentIndex(), str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BrowserWindow::current_dir_path(const QString &dir)
|
||||
{
|
||||
m_dir = dir;
|
||||
@@ -914,39 +914,55 @@ void BrowserWindow::current_dir_path(const QString &dir)
|
||||
settings.setValue("Browser/LastSaveLocation", m_last_open_folder_path);
|
||||
}
|
||||
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
// Below slot will be called when link is required to open in new tab.
|
||||
void BrowserWindow::createNewTabWindow(QWebEnginePage * &p)
|
||||
#ifndef PGADMIN4_USE_WEBENGINE
|
||||
void BrowserWindow::createNewTabWindowKit(QWebPage * &p)
|
||||
{
|
||||
m_addNewTab = new QWidget(m_tabWidget);
|
||||
|
||||
m_addNewGridLayout = new QGridLayout(m_addNewTab);
|
||||
m_addNewGridLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_addNewWebView = new WebViewWindow(m_addNewTab);
|
||||
m_addNewWebView->setPage(new WebEnginePage());
|
||||
m_addNewWebView->setPage(new WebViewPage());
|
||||
m_addNewWebView->setZoomFactor(m_mainWebView->zoomFactor());
|
||||
|
||||
// Register the slot when click on the URL link form main menu bar
|
||||
connect(m_addNewWebView, SIGNAL(linkClicked(const QUrl &)),SLOT(urlLinkClicked(const QUrl &)));
|
||||
// Register the slot when click on the URL link for QWebPage
|
||||
connect(m_addNewWebView->page(), SIGNAL(createTabWindowKit(QWebPage * &)),SLOT(createNewTabWindowKit(QWebPage * &)));
|
||||
|
||||
m_addNewWebView->page()->setForwardUnsupportedContent(true);
|
||||
connect(m_addNewWebView->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(download(const QNetworkRequest &)));
|
||||
connect(m_addNewWebView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*)));
|
||||
m_addNewWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
|
||||
|
||||
m_addNewWebView->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
|
||||
m_addNewWebView->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
|
||||
|
||||
m_widget = new QWidget(m_addNewTab);
|
||||
|
||||
m_toolBtnBack = new QToolButton(m_widget);
|
||||
m_toolBtnBack->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnBack->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnBack->setIcon(QIcon(":/back.png"));
|
||||
m_toolBtnBack->setToolTip(tr("Go back"));
|
||||
m_toolBtnBack->setDisabled(true);
|
||||
m_toolBtnBack->hide();
|
||||
|
||||
m_toolBtnForward = new QToolButton(m_widget);
|
||||
m_toolBtnForward->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnForward->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnForward->setIcon(QIcon(":/forward.png"));
|
||||
m_toolBtnForward->setToolTip(tr("Go forward"));
|
||||
m_toolBtnForward->setDisabled(true);
|
||||
m_toolBtnForward->hide();
|
||||
|
||||
QPushButton *m_btnClose = new QPushButton(m_widget);
|
||||
QToolButton *m_btnClose = new QToolButton(m_widget);
|
||||
m_btnClose->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_btnClose->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_btnClose->setIcon(QIcon(":/close.png"));
|
||||
m_btnClose->setToolTip(tr("Close tab"));
|
||||
|
||||
m_horizontalLayout = new QHBoxLayout(m_widget);
|
||||
m_horizontalLayout->setContentsMargins(0,1,0,0);
|
||||
m_horizontalLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
|
||||
m_horizontalLayout->setSpacing(1);
|
||||
m_horizontalLayout->addWidget(m_toolBtnBack);
|
||||
@@ -956,13 +972,13 @@ void BrowserWindow::createNewTabWindow(QWebEnginePage * &p)
|
||||
connect(m_addNewWebView, SIGNAL(titleChanged(const QString &)), SLOT(tabTitleChanged(const QString &)));
|
||||
|
||||
// Register the slot on toolbutton to show the previous history of web
|
||||
connect(m_toolBtnBack, SIGNAL(clicked()), this, SLOT(goBackPage()));
|
||||
connect(m_toolBtnBack, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoBackPage()));
|
||||
|
||||
// Register the slot on toolbutton to show the next history of web
|
||||
connect(m_toolBtnForward, SIGNAL(clicked()), this, SLOT(goForwardPage()));
|
||||
connect(m_toolBtnForward, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoForwardPage()));
|
||||
|
||||
// Register the slot on close button , added manually
|
||||
connect(m_btnClose, SIGNAL(clicked()), SLOT(closetabs()));
|
||||
connect(m_btnClose, SIGNAL(clicked()), m_tabWidget, SLOT(dockClosetabs()));
|
||||
|
||||
m_addNewGridLayout->addWidget(m_addNewWebView, 0, 0, 1, 1);
|
||||
m_tabWidget->addTab(m_addNewTab, QString());
|
||||
@@ -973,6 +989,75 @@ void BrowserWindow::createNewTabWindow(QWebEnginePage * &p)
|
||||
m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::LeftSide, m_widget);
|
||||
m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::RightSide, m_btnClose);
|
||||
|
||||
m_addNewWebView->setTabIndex((m_tabWidget->count() - 1));
|
||||
m_addNewWebView->page()->setNetworkAccessManager(m_netAccessMan);
|
||||
p = m_addNewWebView->page();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
// Below slot will be called when link is required to open in new tab.
|
||||
void BrowserWindow::createNewTabWindow(QWebEnginePage * &p)
|
||||
{
|
||||
m_addNewTab = new QWidget(m_tabWidget);
|
||||
|
||||
m_addNewGridLayout = new QGridLayout(m_addNewTab);
|
||||
m_addNewGridLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_addNewWebView = new WebViewWindow();
|
||||
m_addNewWebView->setPage(new WebEnginePage());
|
||||
m_addNewWebView->setZoomFactor(m_mainWebView->zoomFactor());
|
||||
|
||||
m_widget = new QWidget(m_addNewTab);
|
||||
|
||||
m_toolBtnBack = new QToolButton(m_widget);
|
||||
m_toolBtnBack->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnBack->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnBack->setIcon(QIcon(":/back.png"));
|
||||
m_toolBtnBack->setToolTip(tr("Go back"));
|
||||
m_toolBtnBack->hide();
|
||||
|
||||
m_toolBtnForward = new QToolButton(m_widget);
|
||||
m_toolBtnForward->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnForward->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnForward->setIcon(QIcon(":/forward.png"));
|
||||
m_toolBtnForward->setToolTip(tr("Go forward"));
|
||||
m_toolBtnForward->hide();
|
||||
|
||||
m_toolBtnClose = new QToolButton(m_widget);
|
||||
m_toolBtnClose->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnClose->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnClose->setIcon(QIcon(":/close.png"));
|
||||
m_toolBtnClose->setToolTip(tr("Close tab"));
|
||||
|
||||
m_horizontalLayout = new QHBoxLayout(m_widget);
|
||||
m_horizontalLayout->setContentsMargins(0,1,0,0);
|
||||
m_horizontalLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
|
||||
m_horizontalLayout->setSpacing(1);
|
||||
m_horizontalLayout->addWidget(m_toolBtnBack);
|
||||
m_horizontalLayout->addWidget(m_toolBtnForward);
|
||||
|
||||
// Register the slot on titleChange so set the tab text accordingly
|
||||
connect(m_addNewWebView, SIGNAL(titleChanged(const QString &)), SLOT(tabTitleChanged(const QString &)));
|
||||
|
||||
// Register the slot on toolbutton to show the previous history of web
|
||||
connect(m_toolBtnBack, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoBackPage()));
|
||||
|
||||
// Register the slot on toolbutton to show the next history of web
|
||||
connect(m_toolBtnForward, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoForwardPage()));
|
||||
|
||||
// Register the slot on close button , added manually
|
||||
connect(m_toolBtnClose, SIGNAL(clicked()), m_tabWidget, SLOT(dockClosetabs()));
|
||||
|
||||
m_addNewGridLayout->addWidget(m_addNewWebView, 0, 0, 1, 1);
|
||||
m_tabWidget->addTab(m_addNewTab, QString());
|
||||
m_tabWidget->tabBar()->setVisible(true);
|
||||
m_tabWidget->setCurrentIndex((m_tabWidget->count() - 1));
|
||||
|
||||
// Set the back and forward button on tab
|
||||
m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::LeftSide, m_widget);
|
||||
m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::RightSide, m_toolBtnClose);
|
||||
|
||||
m_addNewWebView->setTabIndex((m_tabWidget->count() - 1));
|
||||
p = m_addNewWebView->page();
|
||||
}
|
||||
@@ -990,42 +1075,55 @@ void BrowserWindow::urlLinkClicked(const QUrl &name)
|
||||
if (!tabFound)
|
||||
{
|
||||
m_addNewTab = new QWidget(m_tabWidget);
|
||||
|
||||
m_addNewGridLayout = new QGridLayout(m_addNewTab);
|
||||
m_addNewGridLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_addNewWebView = new WebViewWindow(m_addNewTab);
|
||||
|
||||
m_addNewWebView = new WebViewWindow();
|
||||
m_addNewWebView->setZoomFactor(m_mainWebView->zoomFactor());
|
||||
|
||||
// Listen for the download request from the web page
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
m_addNewWebView->setPage(new WebEnginePage());
|
||||
connect(m_addNewWebView->page()->profile(),SIGNAL(downloadRequested(QWebEngineDownloadItem*)),this,SLOT(downloadRequested(QWebEngineDownloadItem*)));
|
||||
#else
|
||||
m_addNewWebView->setPage(new WebViewPage());
|
||||
m_addNewWebView->page()->setForwardUnsupportedContent(true);
|
||||
connect(m_addNewWebView->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(download(const QNetworkRequest &)));
|
||||
connect(m_addNewWebView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*)));
|
||||
|
||||
// Register the slot when click on the URL link form main menu bar
|
||||
connect(m_addNewWebView, SIGNAL(linkClicked(const QUrl &)),SLOT(urlLinkClicked(const QUrl &)));
|
||||
// Register the slot when click on the URL link for QWebPage
|
||||
connect(m_addNewWebView->page(), SIGNAL(createTabWindowKit(QWebPage * &)),SLOT(createNewTabWindowKit(QWebPage * &)));
|
||||
|
||||
m_addNewWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
|
||||
#endif
|
||||
|
||||
m_widget = new QWidget(m_addNewTab);
|
||||
|
||||
m_toolBtnBack = new QToolButton(m_widget);
|
||||
m_toolBtnBack->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnBack->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnBack->setIcon(QIcon(":/back.png"));
|
||||
m_toolBtnBack->setToolTip(tr("Go back"));
|
||||
m_toolBtnBack->setDisabled(true);
|
||||
m_toolBtnBack->hide();
|
||||
|
||||
m_toolBtnForward = new QToolButton(m_widget);
|
||||
m_toolBtnForward->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnForward->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnForward->setIcon(QIcon(":/forward.png"));
|
||||
m_toolBtnForward->setToolTip(tr("Go forward"));
|
||||
m_toolBtnForward->setDisabled(true);
|
||||
m_toolBtnForward->hide();
|
||||
|
||||
QPushButton *m_btnClose = new QPushButton(m_widget);
|
||||
m_btnClose->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_btnClose->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_btnClose->setIcon(QIcon(":/close.png"));
|
||||
m_btnClose->setToolTip(tr("Close tab"));
|
||||
m_toolBtnClose = new QToolButton(m_widget);
|
||||
m_toolBtnClose->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnClose->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnClose->setIcon(QIcon(":/close.png"));
|
||||
m_toolBtnClose->setToolTip(tr("Close tab"));
|
||||
|
||||
m_horizontalLayout = new QHBoxLayout(m_widget);
|
||||
m_horizontalLayout->setContentsMargins(0,1,0,0);
|
||||
m_horizontalLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
|
||||
m_horizontalLayout->setSpacing(1);
|
||||
m_horizontalLayout->addWidget(m_toolBtnBack);
|
||||
@@ -1035,13 +1133,13 @@ void BrowserWindow::urlLinkClicked(const QUrl &name)
|
||||
connect(m_addNewWebView, SIGNAL(titleChanged(const QString &)), SLOT(tabTitleChanged(const QString &)));
|
||||
|
||||
// Register the slot on toolbutton to show the previous history of web
|
||||
connect(m_toolBtnBack, SIGNAL(clicked()), this, SLOT(goBackPage()));
|
||||
connect(m_toolBtnBack, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoBackPage()));
|
||||
|
||||
// Register the slot on toolbutton to show the next history of web
|
||||
connect(m_toolBtnForward, SIGNAL(clicked()), this, SLOT(goForwardPage()));
|
||||
connect(m_toolBtnForward, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoForwardPage()));
|
||||
|
||||
// Register the slot on close button , added manually
|
||||
connect(m_btnClose, SIGNAL(clicked()), SLOT(closetabs()));
|
||||
connect(m_toolBtnClose, SIGNAL(clicked()), m_tabWidget, SLOT(dockClosetabs()));
|
||||
|
||||
m_addNewGridLayout->addWidget(m_addNewWebView, 0, 0, 1, 1);
|
||||
m_tabWidget->addTab(m_addNewTab, QString());
|
||||
@@ -1050,7 +1148,7 @@ void BrowserWindow::urlLinkClicked(const QUrl &name)
|
||||
|
||||
// Set the back and forward button on tab
|
||||
m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::LeftSide, m_widget);
|
||||
m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::RightSide, m_btnClose);
|
||||
m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::RightSide, m_toolBtnClose);
|
||||
|
||||
m_addNewWebView->setFirstLoadURL(name.host());
|
||||
m_addNewWebView->setTabIndex((m_tabWidget->count() - 1));
|
||||
@@ -1074,8 +1172,8 @@ void BrowserWindow::about()
|
||||
}
|
||||
|
||||
|
||||
// Zoom in
|
||||
void BrowserWindow::zoomIn()
|
||||
// Set Zoom Level
|
||||
void BrowserWindow::setZoomLevel(int zoomFlag)
|
||||
{
|
||||
int tabCount = 0;
|
||||
WebViewWindow *webviewPtr = NULL;
|
||||
@@ -1097,55 +1195,27 @@ void BrowserWindow::zoomIn()
|
||||
|
||||
if (webviewPtr != NULL)
|
||||
{
|
||||
webviewPtr->setZoomFactor(m_mainWebView->zoomFactor() + 0.1);
|
||||
if (zoomFlag == 1) {
|
||||
webviewPtr->setZoomFactor(m_mainWebView->zoomFactor() + 0.1);
|
||||
}
|
||||
else if (zoomFlag == -1) {
|
||||
webviewPtr->setZoomFactor(m_mainWebView->zoomFactor() - 0.1);
|
||||
}
|
||||
else if(zoomFlag == 0) {
|
||||
webviewPtr->setZoomFactor(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the zoom factor for next time
|
||||
// Set the zoom value for the next time
|
||||
QSettings settings;
|
||||
settings.setValue("Browser/Zoom", m_mainWebView->zoomFactor());
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Zoom out
|
||||
void BrowserWindow::zoomOut()
|
||||
{
|
||||
int tabCount = 0;
|
||||
WebViewWindow *webviewPtr = NULL;
|
||||
|
||||
// Loop through all the tabs
|
||||
for (tabCount = 0; tabCount < m_tabWidget->count(); tabCount++)
|
||||
{
|
||||
QWidget *tab = m_tabWidget->widget(tabCount);
|
||||
if (tab != NULL)
|
||||
{
|
||||
// Find and loop through any child controls
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
// If it's a web view control, set the zoom level based on the main view
|
||||
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
|
||||
if (webviewPtr != NULL)
|
||||
{
|
||||
webviewPtr->setZoomFactor(m_mainWebView->zoomFactor() - 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the zoom factor for next time
|
||||
QSettings settings;
|
||||
settings.setValue("Browser/Zoom", m_mainWebView->zoomFactor());
|
||||
}
|
||||
|
||||
|
||||
// Open an arbitrary URL
|
||||
void BrowserWindow::openUrl()
|
||||
{
|
||||
|
||||
@@ -17,19 +17,31 @@
|
||||
#include "WebViewWindow.h"
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QtWidgets>
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
#include <QtWebEngineWidgets>
|
||||
#include <QtWidgets>
|
||||
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
#include <QtWebEngineWidgets>
|
||||
#else
|
||||
#include <QtWebKitWidgets>
|
||||
#include <QNetworkCookieJar>
|
||||
#include <QNetworkAccessManager>
|
||||
#endif
|
||||
#else
|
||||
#include <QtWebKitWidgets>
|
||||
#include <QMainWindow>
|
||||
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
#include <QtWebEngineView>
|
||||
#else
|
||||
#include <QWebView>
|
||||
#include <QNetworkCookieJar>
|
||||
#include <QNetworkAccessManager>
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#include <QMainWindow>
|
||||
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
#include <QtWebEngineView>
|
||||
#include <QWebEngineHistory>
|
||||
#else
|
||||
#include <QWebView>
|
||||
#endif
|
||||
#include <QWebHistory>
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -43,13 +55,13 @@ class BrowserWindow : public QMainWindow
|
||||
|
||||
public:
|
||||
BrowserWindow(QString url);
|
||||
~BrowserWindow();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
||||
protected slots:
|
||||
void urlLinkClicked(const QUrl &);
|
||||
void closetabs();
|
||||
void tabTitleChanged(const QString &);
|
||||
#ifdef __APPLE__
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
@@ -63,16 +75,12 @@ private slots:
|
||||
void openUrl();
|
||||
void preferences();
|
||||
void about();
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
void setZoomLevel(int zoomFlag);
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
void downloadRequested(QWebEngineDownloadItem *download);
|
||||
#endif
|
||||
|
||||
public slots:
|
||||
void tabIndexChanged(int index);
|
||||
void goBackPage();
|
||||
void goForwardPage();
|
||||
void download(const QNetworkRequest &request);
|
||||
void unsupportedContent(QNetworkReply * reply);
|
||||
void downloadFinished();
|
||||
@@ -83,6 +91,8 @@ public slots:
|
||||
void createNewTabWindow(QWebEnginePage * &);
|
||||
void downloadEngineFileProgress(qint64 , qint64 );
|
||||
void downloadEngineFinished();
|
||||
#else
|
||||
void createNewTabWindowKit(QWebPage * &);
|
||||
#endif
|
||||
|
||||
private:
|
||||
@@ -95,19 +105,23 @@ private:
|
||||
QShortcut *aboutShortcut;
|
||||
QShortcut *zoomInShortcut;
|
||||
QShortcut *zoomOutShortcut;
|
||||
QShortcut *zoomResetShortcut;
|
||||
QSignalMapper *signalMapper;
|
||||
|
||||
QGridLayout *m_tabGridLayout;
|
||||
QGridLayout *m_mainGridLayout;
|
||||
TabWindow *m_tabWidget;
|
||||
DockTabWidget *m_tabWidget;
|
||||
QWidget *m_pgAdminMainTab;
|
||||
|
||||
QWidget *m_addNewTab;
|
||||
QGridLayout *m_addNewGridLayout;
|
||||
WebViewWindow *m_addNewWebView;
|
||||
QHBoxLayout *m_horizontalLayout;
|
||||
|
||||
QWidget *m_widget;
|
||||
QToolButton *m_toolBtnBack;
|
||||
QToolButton *m_toolBtnForward;
|
||||
QToolButton *m_toolBtnClose;
|
||||
|
||||
QString m_downloadFilename;
|
||||
int m_downloadStarted;
|
||||
@@ -118,13 +132,18 @@ private:
|
||||
QString m_last_open_folder_path;
|
||||
QString m_dir;
|
||||
QNetworkReply *m_reply;
|
||||
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
QWebEngineDownloadItem *m_download;
|
||||
#else
|
||||
QNetworkCookieJar *m_cookieJar;
|
||||
QNetworkAccessManager *m_netAccessMan;
|
||||
#endif
|
||||
|
||||
void createActions();
|
||||
void pause(int seconds = 1);
|
||||
int findURLTab(const QUrl &name);
|
||||
void enableDisableToolButtons(WebViewWindow *webViewPtr);
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>pgAdmin 4</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.4.0</string>
|
||||
<string>2.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.4.0</string>
|
||||
<string>2.0.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.9</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
@@ -63,6 +63,7 @@ Server::Server(quint16 port, QString key)
|
||||
|
||||
// Initialise Python
|
||||
Py_NoSiteFlag=1;
|
||||
Py_NoUserSiteDirectory=1;
|
||||
Py_DontWriteBytecodeFlag=1;
|
||||
|
||||
PGA_APP_NAME_UTF8 = PGA_APP_NAME.toUtf8();
|
||||
@@ -271,9 +272,10 @@ void Server::run()
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the port number
|
||||
// Set the port number and key, and force SERVER_MODE off.
|
||||
PyRun_SimpleString(QString("PGADMIN_PORT = %1").arg(m_port).toLatin1());
|
||||
PyRun_SimpleString(QString("PGADMIN_KEY = '%1'").arg(m_key).toLatin1());
|
||||
PyRun_SimpleString(QString("SERVER_MODE = False").toLatin1());
|
||||
|
||||
// Run the app!
|
||||
QByteArray m_appfile_utf8 = m_appfile.toUtf8();
|
||||
|
||||
@@ -20,13 +20,23 @@
|
||||
#include <QWebHistory>
|
||||
#endif
|
||||
|
||||
TabWindow::TabWindow(QWidget *parent) :
|
||||
DockTabWidget *DockTabWidget::mainTabWidget = NULL;
|
||||
|
||||
DockTabWidget::DockTabWidget(QWidget *parent) :
|
||||
QTabWidget(parent)
|
||||
{
|
||||
floatingWidget = NULL;
|
||||
floatingEnabled = false;
|
||||
|
||||
setParent(parent);
|
||||
setTabsClosable(false);
|
||||
setElideMode(Qt::ElideRight);
|
||||
|
||||
// set custom tab bar in tab widget to receive events for docking.
|
||||
setTabBar(new DockTabBar(this));
|
||||
setDocumentMode(true);
|
||||
setAcceptDrops(true);
|
||||
|
||||
// Get the system colours we need
|
||||
QPalette palette = QApplication::palette("QPushButton");
|
||||
QColor activebg = palette.color(QPalette::Button);
|
||||
@@ -45,11 +55,7 @@ TabWindow::TabWindow(QWidget *parent) :
|
||||
"margin-top: 1px; "
|
||||
#ifndef __APPLE__
|
||||
"width: 15em; "
|
||||
#ifdef _WIN32
|
||||
"height: 1.5em; "
|
||||
#else
|
||||
"height: 1em; "
|
||||
#endif
|
||||
#endif
|
||||
"} "
|
||||
"QTabBar::tab:selected { "
|
||||
@@ -64,17 +70,361 @@ TabWindow::TabWindow(QWidget *parent) :
|
||||
"alignment: left; "
|
||||
"}"
|
||||
);
|
||||
#ifdef __APPLE__
|
||||
m_testTabBar = new TabBar();
|
||||
setTabBar(m_testTabBar);
|
||||
#endif
|
||||
|
||||
// Hide the default tab
|
||||
tabBar()->setVisible(false);
|
||||
if (mainTabWidget == NULL)
|
||||
mainTabWidget = this;
|
||||
}
|
||||
|
||||
DockTabWidget::DockTabWidget(DockTabWidget *other, QWidget *parent) :
|
||||
QTabWidget(parent)
|
||||
{
|
||||
setFloatingBaseWidget(other->floatingBaseWidget());
|
||||
setFloatingEnabled(other->isFloatingEnabled());
|
||||
resize(other->size());
|
||||
|
||||
// set custom tab bar in tab widget to receive events for docking.
|
||||
setTabBar(new DockTabBar(this));
|
||||
setDocumentMode(true);
|
||||
setAcceptDrops(true);
|
||||
|
||||
// Get the system colours we need
|
||||
QPalette palette = QApplication::palette("QPushButton");
|
||||
QColor activebg = palette.color(QPalette::Button);
|
||||
QColor activefg = palette.color(QPalette::ButtonText);
|
||||
QColor inactivebg = palette.color(QPalette::Dark);
|
||||
QColor inactivefg = palette.color(QPalette::ButtonText);
|
||||
QColor border = palette.color(QPalette::Mid);
|
||||
|
||||
setStyleSheet(
|
||||
"QTabBar::tab { "
|
||||
"background-color: " + inactivebg.name() + "; "
|
||||
"color: " + inactivefg.name() + "; "
|
||||
"border: 1px solid " + border.name() + "; "
|
||||
"padding: 1px 0px; "
|
||||
"margin-left: 0px; "
|
||||
"margin-top: 1px; "
|
||||
#ifndef __APPLE__
|
||||
"width: 15em; "
|
||||
"height: 1.5em; "
|
||||
#else
|
||||
"font: 11pt; "
|
||||
"width: 19em; "
|
||||
"height: 1.5em; "
|
||||
#endif
|
||||
"} "
|
||||
"QTabBar::tab:selected { "
|
||||
"background-color: " + activebg.name() + "; "
|
||||
"color: " + activefg.name() + "; "
|
||||
"border-bottom-style: none; "
|
||||
"} "
|
||||
"QTabWidget::pane { "
|
||||
"border: 0; "
|
||||
"} "
|
||||
"QTabWidget::tab-bar {"
|
||||
"alignment: left; "
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
void DockTabWidget::setFloatingBaseWidget(QWidget *widget)
|
||||
{
|
||||
floatingWidget = widget;
|
||||
if (floatingEnabled && parentWidget() == 0)
|
||||
setParent(widget);
|
||||
}
|
||||
|
||||
void DockTabWidget::setFloatingEnabled(bool x)
|
||||
{
|
||||
floatingEnabled = x;
|
||||
|
||||
if (parent() == 0)
|
||||
{
|
||||
if (x)
|
||||
setWindowFlags(Qt::Tool);
|
||||
else
|
||||
setWindowFlags(Qt::Window);
|
||||
}
|
||||
}
|
||||
|
||||
// Slot: go back to page and enable/disable toolbutton
|
||||
void DockTabWidget::dockGoBackPage()
|
||||
{
|
||||
WebViewWindow *webviewPtr = NULL;
|
||||
|
||||
QWidget *tab = this->widget(this->currentIndex());
|
||||
if (tab != NULL)
|
||||
{
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
if (webviewPtr != NULL)
|
||||
webviewPtr->back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slot: go forward to page and enable/disable toolbutton
|
||||
void DockTabWidget::dockGoForwardPage()
|
||||
{
|
||||
WebViewWindow *webviewPtr = NULL;
|
||||
|
||||
QWidget *tab = this->widget(this->currentIndex());
|
||||
if (tab != NULL)
|
||||
{
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
if (webviewPtr != NULL)
|
||||
webviewPtr->forward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the tab and remove the memory of the given index tab
|
||||
void DockTabWidget::dockClosetabs()
|
||||
{
|
||||
int totalTabs = 0;
|
||||
QToolButton *btn = NULL;
|
||||
QWidget *tab = NULL;
|
||||
DockTabWidget *l_tab_widget = NULL;
|
||||
|
||||
QObject *senderPtr = QObject::sender();
|
||||
if (senderPtr != NULL)
|
||||
{
|
||||
btn = dynamic_cast<QToolButton*>(senderPtr);
|
||||
if (btn != NULL)
|
||||
{
|
||||
l_tab_widget = dynamic_cast<DockTabWidget*>(btn->parent()->parent());
|
||||
int current_tab_index = 0;
|
||||
|
||||
if (l_tab_widget != NULL)
|
||||
{
|
||||
totalTabs = l_tab_widget->count();
|
||||
for (int loopCount = l_tab_widget->count();loopCount >= 0;loopCount--)
|
||||
{
|
||||
QWidget *l_tab = l_tab_widget->tabBar()->tabButton(loopCount, QTabBar::RightSide);
|
||||
if (l_tab != NULL)
|
||||
{
|
||||
QToolButton *nextBtnPtr = dynamic_cast<QToolButton*>(l_tab);
|
||||
if (nextBtnPtr != NULL && btn != NULL && nextBtnPtr == btn)
|
||||
current_tab_index = loopCount;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QWidget*> widgetList = l_tab_widget->tabBar()->findChildren<QWidget*>();
|
||||
foreach(QWidget* widgetPtr, widgetList)
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
QToolButton *toolBtnPtr = dynamic_cast<QToolButton*>(widgetPtr);
|
||||
if (toolBtnPtr != NULL && toolBtnPtr == btn)
|
||||
{
|
||||
tab = l_tab_widget->widget(current_tab_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tab != NULL)
|
||||
tab->deleteLater();
|
||||
|
||||
// If user close the last tab then close the parent tab widget also.
|
||||
if (totalTabs == 1 && l_tab_widget != NULL)
|
||||
l_tab_widget->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
if (tab != NULL)
|
||||
{
|
||||
WebViewWindow *webviewPtr = NULL;
|
||||
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach (QWidget* widgetPtr, widgetList)
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
if (webviewPtr != NULL)
|
||||
{
|
||||
/* Trigger the action for tab window close so unload event will be called and
|
||||
* resources will be freed properly.
|
||||
* Trigger 'RequestClose' action from Qt5 onwards. Here we have triggerred the action
|
||||
* 'ToggleVideoFullscreen + 1' because we do not know from which webkit
|
||||
* version 'RequestClose' action was added so increment with previous enum value so that
|
||||
* it will be backward webkit version compatible.
|
||||
*/
|
||||
#if QT_VERSION >= 0x050000
|
||||
#ifndef PGADMIN4_USE_WEBENGINE
|
||||
webviewPtr->page()->triggerAction(static_cast<QWebPage::WebAction>(QWebPage::ToggleVideoFullscreen + 1));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if main pgAdmin4 application has only one tab then close tab bar.
|
||||
// Here - check for count 2 because tab will be deleted later.
|
||||
DockTabWidget *mainTab = DockTabWidget::getMainTabWidget();
|
||||
if (mainTab != NULL && l_tab_widget != NULL && l_tab_widget == mainTab && mainTab->count() == 2)
|
||||
mainTab->tabBar()->setVisible(false);
|
||||
}
|
||||
|
||||
// This function is used to set back/forward/close buttons on new tabbar.
|
||||
void DockTabWidget::setButtonsNewTabbar(int index)
|
||||
{
|
||||
QWidget *m_widget = new QWidget();
|
||||
|
||||
QToolButton *m_toolBtnBack = new QToolButton(m_widget);
|
||||
m_toolBtnBack->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnBack->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnBack->setIcon(QIcon(":/back.png"));
|
||||
m_toolBtnBack->setToolTip(tr("Go back"));
|
||||
|
||||
QToolButton *m_toolBtnForward = new QToolButton(m_widget);
|
||||
m_toolBtnForward->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_toolBtnForward->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_toolBtnForward->setIcon(QIcon(":/forward.png"));
|
||||
m_toolBtnForward->setToolTip(tr("Go forward"));
|
||||
|
||||
QToolButton *m_btnClose = new QToolButton(m_widget);
|
||||
m_btnClose->setFixedHeight(PGA_BTN_SIZE);
|
||||
m_btnClose->setFixedWidth(PGA_BTN_SIZE);
|
||||
m_btnClose->setIcon(QIcon(":/close.png"));
|
||||
m_btnClose->setToolTip(tr("Close tab"));
|
||||
|
||||
QHBoxLayout *m_horizontalLayout = new QHBoxLayout(m_widget);
|
||||
m_horizontalLayout->setContentsMargins(0,1,0,0);
|
||||
m_horizontalLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
|
||||
m_horizontalLayout->setSpacing(1);
|
||||
m_horizontalLayout->addWidget(m_toolBtnBack);
|
||||
m_horizontalLayout->addWidget(m_toolBtnForward);
|
||||
|
||||
// Register the slot on toolbutton to show the previous history of web
|
||||
connect(m_toolBtnBack, SIGNAL(clicked()), this, SLOT(dockGoBackPage()));
|
||||
|
||||
// Register the slot on toolbutton to show the next history of web
|
||||
connect(m_toolBtnForward, SIGNAL(clicked()), this, SLOT(dockGoForwardPage()));
|
||||
|
||||
// Register the slot on close button , added manually
|
||||
connect(m_btnClose, SIGNAL(clicked()), SLOT(dockClosetabs()));
|
||||
|
||||
// Register the slot on tab index change
|
||||
connect(this, SIGNAL(currentChanged(int )), this,SLOT(tabIndexChanged(int )));
|
||||
|
||||
// Set the back and forward button on tab
|
||||
this->tabBar()->setTabButton(index, QTabBar::LeftSide, m_widget);
|
||||
this->tabBar()->setTabButton(index, QTabBar::RightSide, m_btnClose);
|
||||
|
||||
// find the webview and hide/show button depending on flag set with web view.
|
||||
QWidget *tab = this->widget(index);
|
||||
if (tab != NULL)
|
||||
{
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
WebViewWindow *webViewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
if (webViewPtr != NULL)
|
||||
{
|
||||
// If user open any file in query tool then "Query -" name will not appear
|
||||
// but it is still query tool so hide the tool button.
|
||||
if (!webViewPtr->getBackForwardButtonHidden())
|
||||
{
|
||||
m_toolBtnBack->show();
|
||||
m_toolBtnForward->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_toolBtnBack->hide();
|
||||
m_toolBtnForward->hide();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used to move to old tab widget to new tab widget.
|
||||
void DockTabWidget::moveTab(DockTabWidget *source, int sourceIndex, DockTabWidget *dest, int destIndex)
|
||||
{
|
||||
if (source == dest && sourceIndex < destIndex)
|
||||
destIndex--;
|
||||
|
||||
QWidget *widget = source->widget(sourceIndex);
|
||||
QString text = source->tabText(sourceIndex);
|
||||
|
||||
source->removeTab(sourceIndex);
|
||||
|
||||
dest->insertTab(destIndex, widget, text);
|
||||
dest->setCurrentIndex(destIndex);
|
||||
}
|
||||
|
||||
// This function is used to decode actual drop event on tab widget.
|
||||
void DockTabWidget::decodeTabDropEvent(QDropEvent *event, DockTabWidget **p_tabWidget, int *p_index)
|
||||
{
|
||||
DockTabBar *tabBar = qobject_cast<DockTabBar *>(event->source());
|
||||
if (!tabBar)
|
||||
{
|
||||
*p_tabWidget = NULL;
|
||||
*p_index = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = event->mimeData()->data(MIMETYPE_TABINDEX);
|
||||
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||
|
||||
int index;
|
||||
stream >> index;
|
||||
|
||||
*p_tabWidget = tabBar->tabWidget();
|
||||
*p_index = index;
|
||||
}
|
||||
|
||||
// This function is used to check event is actually drop event or not.
|
||||
bool DockTabWidget::eventIsTabDrag(QDragEnterEvent *event)
|
||||
{
|
||||
return event->mimeData()->hasFormat(MIMETYPE_TABINDEX) && qobject_cast<DockTabBar *>(event->source());
|
||||
}
|
||||
|
||||
// This function is used to delete tab widget when there is no tab inside.
|
||||
void DockTabWidget::deleteIfEmpty()
|
||||
{
|
||||
if (count() == 0)
|
||||
{
|
||||
emit willBeAutomaticallyDeleted(this);
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
// This is function is used to create another tab widget from parent window.
|
||||
DockTabWidget *DockTabWidget::createAnotherTabWidget(QWidget *parent)
|
||||
{
|
||||
DockTabWidget *tab_widget = new DockTabWidget(this, parent);
|
||||
tab_widget->tabBar()->setVisible(true);
|
||||
return tab_widget;
|
||||
}
|
||||
|
||||
// Check wether tab is insertable or not.
|
||||
bool DockTabWidget::isInsertable(QWidget *widget)
|
||||
{
|
||||
Q_UNUSED(widget)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Hide the close button of given index displayed on right side of tab
|
||||
void TabWindow::enableDisableToolButton(const int &index)
|
||||
void DockTabWidget::enableDisableToolButton(const int &index)
|
||||
{
|
||||
QToolButton *toolBtnPtr = NULL;
|
||||
WebViewWindow *tmpwebviewPtr = NULL;
|
||||
@@ -127,31 +477,52 @@ void TabWindow::enableDisableToolButton(const int &index)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the index of the pushbutton which is requested by user to close the tab
|
||||
int TabWindow::getButtonIndex(QPushButton *btn)
|
||||
// Slot: When the tab index change, hide/show the toolbutton displayed on tab
|
||||
void DockTabWidget::tabIndexChanged(int index)
|
||||
{
|
||||
QPushButton *nextBtnPtr = NULL;
|
||||
int loopCount = 0;
|
||||
int totalTabs = this->count();
|
||||
int tabCount = 0;
|
||||
WebViewWindow *webViewPtr = NULL;
|
||||
|
||||
for (loopCount = 1;loopCount < totalTabs;loopCount++)
|
||||
for (tabCount = 0; tabCount < this->count(); tabCount++)
|
||||
{
|
||||
QWidget *tab = tabBar()->tabButton(loopCount, QTabBar::RightSide);
|
||||
// if main pgAdmin4 application tab then do nothing.
|
||||
if (!QString::compare(this->tabText(tabCount), tr("pgAdmin 4"), Qt::CaseInsensitive))
|
||||
continue;
|
||||
|
||||
QWidget *tab = this->widget(tabCount);
|
||||
if (tab != NULL)
|
||||
{
|
||||
nextBtnPtr = dynamic_cast<QPushButton*>(tab);
|
||||
if (nextBtnPtr != NULL && btn != NULL && nextBtnPtr == btn)
|
||||
return loopCount;
|
||||
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
|
||||
foreach( QWidget* widgetPtr, widgetList )
|
||||
{
|
||||
if (widgetPtr != NULL)
|
||||
{
|
||||
webViewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
|
||||
if (webViewPtr != NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tabCount != index)
|
||||
this->showHideToolButton(tabCount, 0);
|
||||
else
|
||||
{
|
||||
if (!webViewPtr->getBackForwardButtonHidden())
|
||||
this->showHideToolButton(tabCount, 1);
|
||||
else
|
||||
this->showHideToolButton(tabCount, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
// paint the tab text again as index of the tab widget changed.
|
||||
this->tabBar()->update();
|
||||
}
|
||||
|
||||
// Show and Hide the toolbutton once the tab is deselected depending on the option
|
||||
// option 0: Hide the toolButton
|
||||
// option 1: Show the toolButton
|
||||
void TabWindow::showHideToolButton(const int &index, const int &option)
|
||||
void DockTabWidget::showHideToolButton(const int &index, const int &option)
|
||||
{
|
||||
QToolButton *toolBtnPtr = NULL;
|
||||
|
||||
@@ -177,7 +548,166 @@ void TabWindow::showHideToolButton(const int &index, const int &option)
|
||||
}
|
||||
|
||||
// Set the tab tool tip text
|
||||
void TabWindow::setTabToolTipText(const int &index, const QString &toolTipString)
|
||||
void DockTabWidget::setTabToolTipText(const int &index, const QString &toolTipString)
|
||||
{
|
||||
tabBar()->setTabToolTip(index, toolTipString);
|
||||
}
|
||||
|
||||
// Implementation of custom tab bar for docking window.
|
||||
DockTabBar::DockTabBar(DockTabWidget *tabWidget, QWidget *parent) :
|
||||
QTabBar(parent),
|
||||
tab_widget(tabWidget)
|
||||
{
|
||||
isStartingDrag = false;
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
// Insert new tab at specified index.
|
||||
int DockTabBar::insertionIndexAt(const QPoint &pos)
|
||||
{
|
||||
int index = count();
|
||||
for (int i = 0; i < count(); ++i)
|
||||
{
|
||||
QRect rect = tabRect(i);
|
||||
QRect rect1(rect.x(), rect.y(), rect.width() / 2, rect.height());
|
||||
QRect rect2(rect.x() + rect1.width(), rect.y(), rect.width() - rect1.width(), rect.height());
|
||||
if (rect1.contains(pos))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
if (rect2.contains(pos))
|
||||
{
|
||||
index = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// Mouse press event handler for tab drag.
|
||||
void DockTabBar::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
dragStartPos = event->pos();
|
||||
isStartingDrag = true;
|
||||
}
|
||||
QTabBar::mousePressEvent(event);
|
||||
}
|
||||
|
||||
// Mouse move event handler for tab drag.
|
||||
void DockTabBar::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!isStartingDrag)
|
||||
return;
|
||||
|
||||
if ((!event->buttons()) && Qt::LeftButton)
|
||||
return;
|
||||
|
||||
if ((event->pos() - dragStartPos).manhattanLength() < QApplication::startDragDistance())
|
||||
return;
|
||||
|
||||
int index = tabAt(event->pos());
|
||||
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
// Don't allow to drag the pgAdmin4 main tab.
|
||||
if (!QString::compare(tab_widget->tabText(index), tr("pgAdmin 4"), Qt::CaseInsensitive))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// create data
|
||||
QMimeData *mimeData = new QMimeData;
|
||||
|
||||
QByteArray data;
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream << index;
|
||||
|
||||
mimeData->setData(MIMETYPE_TABINDEX, data);
|
||||
|
||||
// create pixmap
|
||||
QRect rect = tabRect(index);
|
||||
QPixmap pixmap(rect.size());
|
||||
|
||||
render(&pixmap, QPoint(), QRegion(rect));
|
||||
|
||||
// exec drag
|
||||
QDrag *drag = new QDrag(this);
|
||||
drag->setMimeData(mimeData);
|
||||
drag->setPixmap(pixmap);
|
||||
QPoint offset = dragStartPos - rect.topLeft();
|
||||
drag->setHotSpot(offset);
|
||||
Qt::DropAction dropAction = drag->exec(Qt::MoveAction | Qt::IgnoreAction);
|
||||
|
||||
if (dropAction != Qt::MoveAction)
|
||||
{
|
||||
DockTabWidget *newTabWidget = tab_widget->createAnotherTabWidget();
|
||||
if (!newTabWidget->isInsertable(tab_widget, index))
|
||||
{
|
||||
newTabWidget->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
DockTabWidget::moveTab(tab_widget, index, newTabWidget, 0);
|
||||
|
||||
newTabWidget->setButtonsNewTabbar(0);
|
||||
newTabWidget->enableDisableToolButton(0);
|
||||
|
||||
QRect newGeometry = newTabWidget->geometry();
|
||||
newGeometry.moveTopLeft(QCursor::pos() - offset);
|
||||
newTabWidget->setGeometry(newGeometry);
|
||||
newTabWidget->show();
|
||||
|
||||
// Check if main pgAdmin4 application has only one tab then close tab bar.
|
||||
// Here - check for count 2 because tab will be deleted later.
|
||||
DockTabWidget *mainTab = DockTabWidget::getMainTabWidget();
|
||||
if (mainTab != NULL && tab_widget != NULL && tab_widget == mainTab && mainTab->count() == 1)
|
||||
mainTab->tabBar()->setVisible(false);
|
||||
}
|
||||
|
||||
tab_widget->deleteIfEmpty();
|
||||
isStartingDrag = false;
|
||||
}
|
||||
|
||||
// Actual tab drag started.
|
||||
void DockTabBar::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (DockTabWidget::eventIsTabDrag(event))
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
// Drag event leave the actual area.
|
||||
void DockTabBar::dragLeaveEvent(QDragLeaveEvent * event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
}
|
||||
|
||||
// Drop event handler for tabbar.
|
||||
void DockTabBar::dropEvent(QDropEvent *event)
|
||||
{
|
||||
DockTabWidget *oldTabWidget = NULL;
|
||||
int oldIndex;
|
||||
DockTabWidget::decodeTabDropEvent(event, &oldTabWidget, &oldIndex);
|
||||
|
||||
if (oldTabWidget && tab_widget && tab_widget->isInsertable(oldTabWidget, oldIndex))
|
||||
{
|
||||
|
||||
int newIndex = insertionIndexAt(event->pos());
|
||||
DockTabWidget::moveTab(oldTabWidget, oldIndex, tab_widget, newIndex);
|
||||
|
||||
// create new back/forward/close buttons and register its events.
|
||||
tab_widget->setButtonsNewTabbar(newIndex);
|
||||
tab_widget->enableDisableToolButton(newIndex);
|
||||
|
||||
// Check if main pgAdmin4 application has only one tab then close tab bar.
|
||||
// Here - check for count 2 because tab will be deleted later.
|
||||
DockTabWidget *mainTab = DockTabWidget::getMainTabWidget();
|
||||
if (mainTab != NULL && oldTabWidget != NULL && oldTabWidget == mainTab && mainTab->count() == 1)
|
||||
mainTab->tabBar()->setVisible(false);
|
||||
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
@@ -22,26 +22,53 @@ const int PGA_BTN_SIZE = 18;
|
||||
const int PGA_BTN_SIZE = 16;
|
||||
#endif
|
||||
|
||||
class TabBar : public QTabBar
|
||||
#include <QTabBar>
|
||||
#include <QTabWidget>
|
||||
|
||||
#define MIMETYPE_TABINDEX "x-paintfield-tabindex"
|
||||
|
||||
class DockTabBar;
|
||||
|
||||
class DockTabWidget : public QTabWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
friend class DockTabBar;
|
||||
public:
|
||||
TabBar(QWidget* parent=0) : QTabBar(parent)
|
||||
|
||||
explicit DockTabWidget(QWidget *parent = 0);
|
||||
|
||||
DockTabWidget(DockTabWidget *other, QWidget *parent = 0);
|
||||
|
||||
// Drop event handlers of parent tab widget.
|
||||
static void moveTab(DockTabWidget *source, int sourceIndex, DockTabWidget *dest, int destIndex);
|
||||
static void decodeTabDropEvent(QDropEvent *event, DockTabWidget **p_tabWidget, int *p_index);
|
||||
static bool eventIsTabDrag(QDragEnterEvent *event);
|
||||
void setButtonsNewTabbar(int index);
|
||||
|
||||
static DockTabWidget *mainTabWidget;
|
||||
static DockTabWidget* getMainTabWidget()
|
||||
{
|
||||
return mainTabWidget;
|
||||
}
|
||||
|
||||
protected:
|
||||
QSize tabSizeHint(int) const
|
||||
void setFloatingBaseWidget(QWidget *widget);
|
||||
QWidget *floatingBaseWidget()
|
||||
{
|
||||
return QSize(250, 24);
|
||||
return floatingWidget;
|
||||
}
|
||||
};
|
||||
|
||||
class TabWindow : public QTabWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TabWindow(QWidget *parent = 0);
|
||||
void setFloatingEnabled(bool x);
|
||||
bool isFloatingEnabled() const
|
||||
{
|
||||
return floatingEnabled;
|
||||
}
|
||||
|
||||
virtual bool isInsertable(QWidget *widget);
|
||||
bool isInsertable(DockTabWidget *other, int index)
|
||||
{
|
||||
return isInsertable(other->widget(index));
|
||||
}
|
||||
virtual DockTabWidget *createAnotherTabWidget(QWidget *parent = 0);
|
||||
|
||||
int getButtonIndex(QPushButton *btn);
|
||||
void showHideToolButton(const int &index,const int &option);
|
||||
@@ -52,8 +79,110 @@ public:
|
||||
return QTabWidget::tabBar();
|
||||
}
|
||||
|
||||
signals:
|
||||
void willBeAutomaticallyDeleted(DockTabWidget *widget);
|
||||
|
||||
public slots:
|
||||
void deleteIfEmpty();
|
||||
void dockClosetabs();
|
||||
void dockGoBackPage();
|
||||
void dockGoForwardPage();
|
||||
void tabIndexChanged(int index);
|
||||
|
||||
private:
|
||||
TabBar *m_testTabBar;
|
||||
QWidget *floatingWidget;
|
||||
bool floatingEnabled;
|
||||
};
|
||||
|
||||
class DockTabBar : public QTabBar
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DockTabBar(DockTabWidget *tabWidget, QWidget *parent = 0);
|
||||
// return tab widget of respective tab bar widget.
|
||||
DockTabWidget *tabWidget()
|
||||
{
|
||||
return tab_widget;
|
||||
}
|
||||
|
||||
protected:
|
||||
// re-implemnted mouse event to detect tab drag started or not.
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void mouseMoveEvent(QMouseEvent *event);
|
||||
|
||||
// re-implemnted drag-drop event for docking of tabs.
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void dropEvent(QDropEvent *event);
|
||||
void dragLeaveEvent(QDragLeaveEvent * event);
|
||||
|
||||
// re-implemented paint event to draw the text on tab bar of tab widget control.
|
||||
void paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
bool isToolBtnVisible = false;
|
||||
|
||||
DockTabWidget *l_tab_widget = dynamic_cast<DockTabWidget*>(this->parent());
|
||||
|
||||
if (l_tab_widget != NULL)
|
||||
{
|
||||
int current_index = l_tab_widget->currentIndex();
|
||||
QStylePainter painter(this);
|
||||
for(int i = 0; i < l_tab_widget->count(); ++i)
|
||||
{
|
||||
QString str = l_tab_widget->tabText(i);
|
||||
if (!str.startsWith("pgAdmin 4") && !str.startsWith("Query -") && !str.startsWith("Debugger"))
|
||||
isToolBtnVisible = true;
|
||||
|
||||
QStyleOptionTab option;
|
||||
initStyleOption(&option, i);
|
||||
QString tempText = this->tabText(i);
|
||||
if (tempText.length() > 28)
|
||||
{
|
||||
tempText = tempText.mid(0,27);
|
||||
tempText += QString("...");
|
||||
}
|
||||
|
||||
QRect rect(option.rect);
|
||||
|
||||
// If toolButton is visible then only draw text after tool button pixel area.
|
||||
// If tool button is not visible - draw the text after margin of 10px.
|
||||
if (isToolBtnVisible)
|
||||
{
|
||||
if ((current_index != -1) && i == current_index)
|
||||
{
|
||||
if (str.startsWith("Query -") || str.startsWith("Debugger"))
|
||||
rect.setX(option.rect.x() + 10);
|
||||
else
|
||||
rect.setX(option.rect.x() + 45);
|
||||
}
|
||||
else
|
||||
rect.setX(option.rect.x() + 10);
|
||||
}
|
||||
else
|
||||
rect.setX(option.rect.x() + 10);
|
||||
|
||||
rect.setY(option.rect.y() + 7);
|
||||
|
||||
option.text = QString();
|
||||
|
||||
painter.drawControl(QStyle::CE_TabBarTab, option);
|
||||
painter.drawItemText(rect, 0, palette(), 1, tempText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
QSize tabSizeHint(int) const
|
||||
{
|
||||
return QSize(250, 26);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
int insertionIndexAt(const QPoint &pos);
|
||||
DockTabWidget *tab_widget;
|
||||
bool isStartingDrag;
|
||||
QPoint dragStartPos;
|
||||
};
|
||||
|
||||
#endif // TABWINDOW_H
|
||||
@@ -13,6 +13,14 @@
|
||||
|
||||
// App headers
|
||||
#include "WebViewWindow.h"
|
||||
#include "TabWindow.h"
|
||||
|
||||
#ifndef PGADMIN4_USE_WEBENGINE
|
||||
#include <QWebPage>
|
||||
#include <QNetworkRequest>
|
||||
#endif
|
||||
|
||||
WebViewWindow *WebViewWindow::mainWebViewWindow = NULL;
|
||||
|
||||
// Override QWebEnginePage to handle link delegation
|
||||
#ifdef PGADMIN4_USE_WEBENGINE
|
||||
@@ -46,6 +54,85 @@ WebViewWindow::WebViewWindow(QWidget *parent) :
|
||||
{
|
||||
m_url = QString("");
|
||||
m_tabIndex = 0;
|
||||
m_backForwardBtnHide = false;
|
||||
|
||||
// Accept drop event for only main pgAdmin4 application window.
|
||||
if (mainWebViewWindow == NULL)
|
||||
mainWebViewWindow = this;
|
||||
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
// Actual tab drag started.
|
||||
void WebViewWindow::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
//DockTabWidget *mainTabWidget = DockTabWidget::getMainTabWidget();
|
||||
//if (this->parent()->parent()->parent() == mainTabWidget)
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void WebViewWindow::dragMoveEvent(QDragMoveEvent *event)
|
||||
{
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
// Drop event handler for tabbar.
|
||||
void WebViewWindow::dropEvent(QDropEvent *event)
|
||||
{
|
||||
DockTabWidget *oldTabWidget;
|
||||
int oldIndex;
|
||||
DockTabWidget::decodeTabDropEvent(event, &oldTabWidget, &oldIndex);
|
||||
|
||||
//DockTabWidget *mainTabWidget = DockTabWidget::getMainTabWidget();
|
||||
DockTabWidget *mainTabWidget = dynamic_cast<DockTabWidget*>(this->parent()->parent()->parent());
|
||||
|
||||
if (oldTabWidget && mainTabWidget && oldTabWidget != mainTabWidget)
|
||||
//if (oldTabWidget && mainTabWidget)
|
||||
{
|
||||
mainTabWidget->tabBar()->setVisible(true);
|
||||
QPoint pos = event->pos();
|
||||
int index = mainTabWidget->tabBar()->count();
|
||||
for (int i = 0; i < mainTabWidget->tabBar()->count(); ++i)
|
||||
{
|
||||
QRect rect = mainTabWidget->tabBar()->tabRect(i);
|
||||
QRect rect1(rect.x(), rect.y(), rect.width() / 2, rect.height());
|
||||
QRect rect2(rect.x() + rect1.width(), rect.y(), rect.width() - rect1.width(), rect.height());
|
||||
if (rect1.contains(pos))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
if (rect2.contains(pos))
|
||||
{
|
||||
index = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DockTabWidget::moveTab(oldTabWidget, oldIndex, mainTabWidget, index);
|
||||
|
||||
// create new back/forward/close buttons and register its events.
|
||||
mainTabWidget->setButtonsNewTabbar(index);
|
||||
mainTabWidget->enableDisableToolButton(index);
|
||||
|
||||
// Check if main pgAdmin4 application has only one tab then close tab bar.
|
||||
// Here - check for count 2 because tab will be deleted later.
|
||||
DockTabWidget *mainTab = DockTabWidget::getMainTabWidget();
|
||||
if (mainTab != NULL && oldTabWidget != NULL && oldTabWidget == mainTab && mainTab->count() == 1)
|
||||
mainTab->tabBar()->setVisible(false);
|
||||
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void WebViewWindow::setBackForwardButtonHidden(const bool hideButton)
|
||||
{
|
||||
m_backForwardBtnHide = hideButton;
|
||||
}
|
||||
|
||||
bool WebViewWindow::getBackForwardButtonHidden() const
|
||||
{
|
||||
return m_backForwardBtnHide;
|
||||
}
|
||||
|
||||
void WebViewWindow::setFirstLoadURL(const QString &url)
|
||||
@@ -67,3 +154,42 @@ int WebViewWindow::getTabIndex() const
|
||||
{
|
||||
return m_tabIndex;
|
||||
}
|
||||
|
||||
#ifndef PGADMIN4_USE_WEBENGINE
|
||||
WebViewPage::WebViewPage(QObject *parent)
|
||||
: QWebPage(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool WebViewPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
|
||||
{
|
||||
Q_UNUSED(type);
|
||||
Q_UNUSED(request);
|
||||
Q_UNUSED(frame);
|
||||
return true;
|
||||
}
|
||||
|
||||
QWebPage *WebViewPage::createWindow(QWebPage::WebWindowType type)
|
||||
{
|
||||
if (type == QWebPage::WebBrowserWindow)
|
||||
{
|
||||
QWebPage *_page = NULL;
|
||||
emit createTabWindowKit(_page);
|
||||
return _page;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool WebViewPage::javaScriptConfirm(QWebFrame * frame, const QString & msg)
|
||||
{
|
||||
// If required, override the QDialog to give custom confirmation message to user.
|
||||
Q_UNUSED(frame);
|
||||
Q_UNUSED(msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
WebViewPage::~WebViewPage()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -51,11 +51,45 @@ public:
|
||||
QString getFirstLoadURL() const;
|
||||
void setTabIndex(const int &tabIndex);
|
||||
int getTabIndex() const;
|
||||
void setBackForwardButtonHidden(const bool hideButton);
|
||||
bool getBackForwardButtonHidden() const;
|
||||
|
||||
// Store main webview window of pgAdmin4 application.
|
||||
static WebViewWindow *mainWebViewWindow;
|
||||
static WebViewWindow* getMainWebViewWindow()
|
||||
{
|
||||
return mainWebViewWindow;
|
||||
}
|
||||
|
||||
protected:
|
||||
// re-implemnted drag-drop event for docking of tabs.
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void dragMoveEvent(QDragMoveEvent *event);
|
||||
void dropEvent(QDropEvent *event);
|
||||
|
||||
private:
|
||||
QString m_url;
|
||||
int m_tabIndex;
|
||||
|
||||
bool m_backForwardBtnHide;
|
||||
};
|
||||
|
||||
#ifndef PGADMIN4_USE_WEBENGINE
|
||||
class WebViewPage : public QWebPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WebViewPage(QObject *parent = 0);
|
||||
~WebViewPage();
|
||||
|
||||
protected:
|
||||
virtual bool acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type);
|
||||
QWebPage *createWindow(QWebPage::WebWindowType type);
|
||||
bool javaScriptConfirm(QWebFrame * frame, const QString & msg);
|
||||
|
||||
signals:
|
||||
void createTabWindowKit(QWebPage * &);
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // WEBVIEWWINDOW_H
|
||||
|
Before Width: | Height: | Size: 448 B After Width: | Height: | Size: 290 B |
|
Before Width: | Height: | Size: 587 B After Width: | Height: | Size: 346 B |
|
Before Width: | Height: | Size: 445 B After Width: | Height: | Size: 270 B |
@@ -175,6 +175,15 @@ void delay( int milliseconds )
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
/*
|
||||
* Before starting main application, need to set 'QT_X11_NO_MITSHM=1'
|
||||
* to make the runtime work with IBM PPC machine.
|
||||
*/
|
||||
#if defined (Q_OS_LINUX)
|
||||
QByteArray val("1");
|
||||
qputenv("QT_X11_NO_MITSHM", val);
|
||||
#endif
|
||||
|
||||
// Create the QT application
|
||||
QApplication app(argc, argv);
|
||||
|
||||
@@ -183,6 +192,48 @@ int main(int argc, char * argv[])
|
||||
QCoreApplication::setOrganizationDomain("pgadmin.org");
|
||||
QCoreApplication::setApplicationName(PGA_APP_NAME.toLower().replace(" ", ""));
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
// Set high DPI pixmap to display icons clear on Qt widget.
|
||||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
|
||||
/* In windows and linux, it is required to set application level proxy
|
||||
* becuase 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.
|
||||
*/
|
||||
#ifndef PGADMIN4_USE_WEBENGINE
|
||||
#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
|
||||
#endif
|
||||
|
||||
#ifndef PGADMIN4_USE_WEBENGINE
|
||||
#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
|
||||
#endif
|
||||
|
||||
// Display the spash screen
|
||||
QSplashScreen *splash = new QSplashScreen();
|
||||
splash->setPixmap(QPixmap(":/splash.png"));
|
||||
@@ -199,6 +250,11 @@ int main(int argc, char * argv[])
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
QTcpSocket socket;
|
||||
|
||||
#if QT_VERSION >= 0x050900
|
||||
socket.setProxy(QNetworkProxy::NoProxy);
|
||||
#endif
|
||||
|
||||
socket.bind(0, QTcpSocket::ShareAddress);
|
||||
#else
|
||||
QUdpSocket socket;
|
||||
@@ -248,10 +304,6 @@ int main(int argc, char * argv[])
|
||||
|
||||
server->start();
|
||||
|
||||
// This is a hack. Wait a second and then check to see if the server thread
|
||||
// is still running. If it's not, we probably had a startup error
|
||||
delay(1000);
|
||||
|
||||
// Any errors?
|
||||
if (server->isFinished() || server->getError().length() > 0)
|
||||
{
|
||||
@@ -295,30 +347,38 @@ int main(int argc, char * argv[])
|
||||
|
||||
|
||||
// Generate the app server URL
|
||||
QString appServerUrl = QString("http://localhost:%1/?key=%2").arg(port).arg(key);
|
||||
QString appServerUrl = QString("http://127.0.0.1:%1/?key=%2").arg(port).arg(key);
|
||||
|
||||
// Read the server connection timeout from the registry or set the default timeout.
|
||||
QSettings settings;
|
||||
int timeout = settings.value("ConnectionTimeout", 30).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.
|
||||
int attempt = 0;
|
||||
while (attempt++ < 30)
|
||||
|
||||
QTime endTime = QTime::currentTime().addSecs(timeout);
|
||||
bool alive = false;
|
||||
|
||||
while(QTime::currentTime() <= endTime)
|
||||
{
|
||||
bool alive = PingServer(QUrl(appServerUrl));
|
||||
alive = PingServer(QUrl(appServerUrl));
|
||||
|
||||
if (alive)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (attempt == 30)
|
||||
{
|
||||
splash->finish(NULL);
|
||||
QString error(QWidget::tr("The application server could not be contacted."));
|
||||
QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);
|
||||
delay(200);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
// Attempt to connect one more time in case of a long network timeout while looping
|
||||
if(!alive && !PingServer(QUrl(appServerUrl)))
|
||||
{
|
||||
splash->finish(NULL);
|
||||
QString error(QWidget::tr("The application server could not be contacted."));
|
||||
QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
delay(1000);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Create & show the main window
|
||||
@@ -329,6 +389,16 @@ int main(int argc, char * argv[])
|
||||
|
||||
// Go!
|
||||
splash->finish(NULL);
|
||||
|
||||
// Set global application stylesheet.
|
||||
QFile file(":/qss/pgadmin4.qss");
|
||||
if(file.open(QFile::ReadOnly))
|
||||
{
|
||||
QString StyleSheet = QLatin1String(file.readAll());
|
||||
qApp->setStyleSheet(StyleSheet);
|
||||
file.close();
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION = 1.4.0.0
|
||||
VERSION = 2.0.0.0
|
||||
QMAKE_TARGET_COMPANY = "The pgAdmin Development Team"
|
||||
QMAKE_TARGET_PRODUCT = "pgAdmin 4"
|
||||
QMAKE_TARGET_DESCRIPTION = "pgAdmin 4 Desktop Runtime"
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
<file>forward.png</file>
|
||||
<file>close.png</file>
|
||||
<file>splash.png</file>
|
||||
<file>qss/pgadmin4.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -0,0 +1,3 @@
|
||||
QTabBar::tab {
|
||||
background-color: #E8E8E8;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,30 @@
|
||||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
# We recommend you to keep these unchanged
|
||||
[*]
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 80
|
||||
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
|
||||
[*.js]
|
||||
indent_size = 2
|
||||
|
||||
[*.css]
|
||||
indent_size = 2
|
||||
|
||||
# Matches the exact file package.json
|
||||
[{package.json}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -0,0 +1,48 @@
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'amd': true,
|
||||
'jasmine': true,
|
||||
},
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
"plugin:react/recommended",
|
||||
],
|
||||
'parserOptions': {
|
||||
'ecmaFeatures': {
|
||||
'experimentalObjectRestSpread': true,
|
||||
'jsx': true
|
||||
},
|
||||
'sourceType': 'module'
|
||||
},
|
||||
'plugins': [
|
||||
'react'
|
||||
],
|
||||
'globals': {
|
||||
'_': true,
|
||||
'module': true,
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
2
|
||||
],
|
||||
'linebreak-style': [
|
||||
'error',
|
||||
'unix'
|
||||
],
|
||||
'quotes': [
|
||||
'error',
|
||||
'single'
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'always-multiline'
|
||||
],
|
||||
}
|
||||
};
|
||||
@@ -11,9 +11,15 @@
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
import builtins
|
||||
else:
|
||||
import __builtin__ as builtins
|
||||
|
||||
|
||||
# We need to include the root directory in sys.path to ensure that we can
|
||||
# find everything we need when running in the standalone runtime.
|
||||
@@ -29,7 +35,7 @@ from pgadmin.utils import env, IS_PY2, IS_WIN, fs_short_path
|
||||
|
||||
# Name of the application to display in the UI
|
||||
APP_NAME = 'pgAdmin 4'
|
||||
APP_ICON = 'icon-postgres-alt'
|
||||
APP_ICON = 'pg-icon'
|
||||
|
||||
##########################################################################
|
||||
# Application settings
|
||||
@@ -44,8 +50,8 @@ APP_ICON = 'icon-postgres-alt'
|
||||
#
|
||||
|
||||
# Application version number components
|
||||
APP_RELEASE = 1
|
||||
APP_REVISION = 4
|
||||
APP_RELEASE = 2
|
||||
APP_REVISION = 0
|
||||
|
||||
# Application version suffix, e.g. 'beta1', 'dev'. Usually an empty string
|
||||
# for GA releases.
|
||||
@@ -54,9 +60,7 @@ APP_SUFFIX = ''
|
||||
# Numeric application version for upgrade checks. Should be in the format:
|
||||
# [X]XYYZZ, where X is the release version, Y is the revision, with a leading
|
||||
# zero if needed, and Z represents the suffix, with a leading zero if needed
|
||||
# Note that we messed this up in v1.x, where the format is [X]XYZZZ. This
|
||||
# should be fixed for v2.x!!
|
||||
APP_VERSION_INT = 14001
|
||||
APP_VERSION_INT = 20003
|
||||
|
||||
# DO NOT CHANGE!
|
||||
# The application version string, constructed from the components
|
||||
@@ -93,46 +97,6 @@ MODULE_BLACKLIST = ['test']
|
||||
NODE_BLACKLIST = []
|
||||
|
||||
|
||||
# Data directory for storage of config settings etc. This shouldn't normally
|
||||
# need to be changed - it's here as various other settings depend on it.
|
||||
if IS_WIN:
|
||||
# Use the short path on windows
|
||||
DATA_DIR = os.path.realpath(
|
||||
os.path.join(fs_short_path(env('APPDATA')), u"pgAdmin")
|
||||
)
|
||||
else:
|
||||
DATA_DIR = os.path.realpath(os.path.expanduser(u'~/.pgadmin/'))
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Log settings
|
||||
##########################################################################
|
||||
|
||||
# Debug mode?
|
||||
DEBUG = False
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
# Application log level - one of:
|
||||
# CRITICAL 50
|
||||
# ERROR 40
|
||||
# WARNING 30
|
||||
# SQL 25
|
||||
# INFO 20
|
||||
# DEBUG 10
|
||||
# NOTSET 0
|
||||
CONSOLE_LOG_LEVEL = logging.WARNING
|
||||
FILE_LOG_LEVEL = logging.WARNING
|
||||
|
||||
# Log format.
|
||||
CONSOLE_LOG_FORMAT = '%(asctime)s: %(levelname)s\t%(name)s:\t%(message)s'
|
||||
FILE_LOG_FORMAT = '%(asctime)s: %(levelname)s\t%(name)s:\t%(message)s'
|
||||
|
||||
# Log file name
|
||||
LOG_FILE = os.path.join(DATA_DIR, 'pgadmin4.log')
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Server settings
|
||||
##########################################################################
|
||||
@@ -142,13 +106,25 @@ LOG_FILE = os.path.join(DATA_DIR, 'pgadmin4.log')
|
||||
# default login.
|
||||
#
|
||||
# DO NOT DISABLE SERVER MODE IF RUNNING ON A WEBSERVER!!
|
||||
SERVER_MODE = True
|
||||
#
|
||||
# We only set SERVER_MODE if it's not already set. That's to allow the
|
||||
# runtime to force it to False.
|
||||
#
|
||||
# NOTE: If you change the value of SERVER_MODE in an included config file,
|
||||
# you may also need to redefine any values below that are derived
|
||||
# from it, notably various paths such as LOG_FILE and anything
|
||||
# using DATA_DIR.
|
||||
|
||||
if builtins.SERVER_MODE is None:
|
||||
SERVER_MODE = True
|
||||
else:
|
||||
SERVER_MODE = builtins.SERVER_MODE
|
||||
|
||||
# User ID (email address) to use for the default user in desktop mode.
|
||||
# The default should be fine here, as it's not exposed in the app.
|
||||
DESKTOP_USER = 'pgadmin4@pgadmin.org'
|
||||
|
||||
# This configuration otion allows the user to host the application on a LAN
|
||||
# This option allows the user to host the application on a LAN
|
||||
# Default hosting is on localhost (DEFAULT_SERVER='localhost').
|
||||
# To host pgAdmin4 over LAN set DEFAULT_SERVER='0.0.0.0' (or a specific
|
||||
# adaptor address.
|
||||
@@ -156,7 +132,7 @@ DESKTOP_USER = 'pgadmin4@pgadmin.org'
|
||||
# NOTE: This is NOT recommended for production use, only for debugging
|
||||
# or testing. Production installations should be run as a WSGI application
|
||||
# behind Apache HTTPD.
|
||||
DEFAULT_SERVER = 'localhost'
|
||||
DEFAULT_SERVER = '127.0.0.1'
|
||||
|
||||
# The default port on which the app server will listen if not set in the
|
||||
# environment by the runtime
|
||||
@@ -177,6 +153,52 @@ SECURITY_PASSWORD_HASH = 'pbkdf2_sha512'
|
||||
# has no effect on <= Python 2.7.
|
||||
MINIFY_PAGE = True
|
||||
|
||||
# Data directory for storage of config settings etc. This shouldn't normally
|
||||
# need to be changed - it's here as various other settings depend on it.
|
||||
# On Windows, we always store data in %APPDATA%\pgAdmin. On other platforms,
|
||||
# if we're in server mode we use /var/lib/pgadmin, otherwise ~/.pgadmin
|
||||
if IS_WIN:
|
||||
# Use the short path on windows
|
||||
DATA_DIR = os.path.realpath(
|
||||
os.path.join(fs_short_path(env('APPDATA')), u"pgAdmin")
|
||||
)
|
||||
else:
|
||||
if SERVER_MODE:
|
||||
DATA_DIR = '/var/lib/pgadmin'
|
||||
else:
|
||||
DATA_DIR = os.path.realpath(os.path.expanduser(u'~/.pgadmin/'))
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Log settings
|
||||
##########################################################################
|
||||
|
||||
# Debug mode?
|
||||
DEBUG = False
|
||||
|
||||
# Application log level - one of:
|
||||
# CRITICAL 50
|
||||
# ERROR 40
|
||||
# WARNING 30
|
||||
# SQL 25
|
||||
# INFO 20
|
||||
# DEBUG 10
|
||||
# NOTSET 0
|
||||
CONSOLE_LOG_LEVEL = logging.WARNING
|
||||
FILE_LOG_LEVEL = logging.WARNING
|
||||
|
||||
# Log format.
|
||||
CONSOLE_LOG_FORMAT = '%(asctime)s: %(levelname)s\t%(name)s:\t%(message)s'
|
||||
FILE_LOG_FORMAT = '%(asctime)s: %(levelname)s\t%(name)s:\t%(message)s'
|
||||
|
||||
# Log file name. This goes in the data directory, except on non-Windows
|
||||
# platforms in server mode.
|
||||
if SERVER_MODE and not IS_WIN:
|
||||
LOG_FILE = '/var/log/pgadmin/pgadmin4.log'
|
||||
else:
|
||||
LOG_FILE = os.path.join(DATA_DIR, 'pgadmin4.log')
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Server Connection Driver Settings
|
||||
##########################################################################
|
||||
@@ -299,14 +321,13 @@ STORAGE_DIR = os.path.join(DATA_DIR, 'storage')
|
||||
##########################################################################
|
||||
DEFAULT_BINARY_PATHS = {
|
||||
"pg": "",
|
||||
"ppas": ""
|
||||
"ppas": "",
|
||||
"gpdb": ""
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
# Test settings - used primarily by the regression suite, not for users
|
||||
##########################################################################
|
||||
# Set default testing mode
|
||||
TESTING_MODE = False
|
||||
|
||||
# The default path for SQLite database for testing
|
||||
TEST_SQLITE_PATH = os.path.join(DATA_DIR, 'test_pgadmin4.db')
|
||||
@@ -323,6 +344,12 @@ THREADED_MODE = True
|
||||
##########################################################################
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
##########################################################################
|
||||
# Number of records to fetch in one batch in query tool when query result
|
||||
# set is large.
|
||||
##########################################################################
|
||||
ON_DEMAND_RECORD_COUNT = 1000
|
||||
|
||||
##########################################################################
|
||||
# Local config settings
|
||||
##########################################################################
|
||||
|
||||
@@ -1,62 +1,57 @@
|
||||
// Karma configuration
|
||||
// Generated on Wed Mar 01 2017 14:19:28 GMT-0500 (EST)
|
||||
const webpackConfig = require('./webpack.test.config.js');
|
||||
|
||||
module.exports = function(config) {
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['jasmine', 'requirejs'],
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'regression/javascript/test-main.js',
|
||||
{pattern: 'regression/javascript/**/*.js', included: false},
|
||||
{pattern: 'pgadmin/static/vendor/**/*.js', included: false},
|
||||
{pattern: 'pgadmin/static/js/**/*.js', included: false}
|
||||
frameworks: ['jasmine'],
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
plugins: [
|
||||
'karma-webpack',
|
||||
'karma-phantomjs-launcher',
|
||||
'karma-jasmine',
|
||||
'karma-jasmine-html-reporter',
|
||||
],
|
||||
files: [
|
||||
{pattern: 'pgadmin/static/**/*.js', included: false},
|
||||
{pattern: 'pgadmin/static/vendor/**/*.js', included: false},
|
||||
{pattern: 'pgadmin/browser/static/js/**/*.js', included: false},
|
||||
'regression/javascript/**/*.jsx',
|
||||
'regression/javascript/**/*.js',
|
||||
'pgadmin/static/bundle/slickgrid.js',
|
||||
],
|
||||
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [
|
||||
'pgadmin/static/js/pgadmin.js',
|
||||
'pgadmin/static/vendor/**/*[Tt]est.js',
|
||||
'pgadmin/static/vendor/**/*[Ss]pec.js'
|
||||
'pgadmin/static/vendor/**/*[Ss]pec.js',
|
||||
],
|
||||
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
'regression/javascript/**/*.js': ['webpack'],
|
||||
'regression/javascript/**/*.jsx': ['webpack'],
|
||||
'pgadmin/static/bundle/slickgrid.js': ['webpack'],
|
||||
},
|
||||
|
||||
webpack: webpackConfig,
|
||||
webpackMiddleware: {
|
||||
stats: 'errors-only',
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['progress'],
|
||||
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
logLevel: config.LOG_WARN,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
autoWatch: true,
|
||||
|
||||
|
||||
// start these browsers
|
||||
@@ -66,10 +61,10 @@ module.exports = function(config) {
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: true,
|
||||
singleRun: false,
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: Infinity
|
||||
})
|
||||
}
|
||||
concurrency: Infinity,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
@@ -0,0 +1,94 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
from __future__ import with_statement
|
||||
from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
import logging
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
logger = logging.getLogger('alembic.env')
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
from flask import current_app
|
||||
config.set_main_option('sqlalchemy.url',
|
||||
current_app.config.get('SQLALCHEMY_DATABASE_URI'))
|
||||
target_metadata = current_app.extensions['migrate'].db.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(url=url)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
|
||||
# this callback is used to prevent an auto-migration from being generated
|
||||
# when there are no changes to the schema
|
||||
# reference: http://alembic.readthedocs.org/en/latest/cookbook.html
|
||||
def process_revision_directives(context, revision, directives):
|
||||
if getattr(config.cmd_opts, 'autogenerate', False):
|
||||
script = directives[0]
|
||||
if script.upgrade_ops.is_empty():
|
||||
directives[:] = []
|
||||
logger.info('No changes in schema detected.')
|
||||
|
||||
engine = engine_from_config(config.get_section(config.config_ini_section),
|
||||
prefix='sqlalchemy.',
|
||||
poolclass=pool.NullPool)
|
||||
|
||||
connection = engine.connect()
|
||||
context.configure(connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
process_revision_directives=process_revision_directives,
|
||||
**current_app.extensions['migrate'].configure_args)
|
||||
|
||||
try:
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
@@ -0,0 +1,33 @@
|
||||
##########################################################################
|
||||
##
|
||||
## pgAdmin 4 - PostgreSQL Tools
|
||||
##
|
||||
## Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
## This software is released under the PostgreSQL Licence
|
||||
##
|
||||
##########################################################################
|
||||
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
depends_on = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
||||
@@ -0,0 +1,241 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""Update DB to version 14
|
||||
|
||||
Revision ID: 09d53fca90c7
|
||||
Revises: fdc58d9bd449
|
||||
Create Date: 2017-03-13 12:27:30.543908
|
||||
|
||||
"""
|
||||
import base64
|
||||
import os
|
||||
import sys
|
||||
|
||||
import config
|
||||
from pgadmin.model import db, Server
|
||||
from pgadmin.setup import get_version
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
revision = '09d53fca90c7'
|
||||
down_revision = 'fdc58d9bd449'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
version = get_version()
|
||||
# Changes introduced in schema version 2
|
||||
if version < 2:
|
||||
# Create the 'server' table
|
||||
db.metadata.create_all(db.engine, tables=[Server.__table__])
|
||||
if version < 3:
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN comment TEXT(1024)'
|
||||
)
|
||||
if version < 4:
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN password TEXT(64)'
|
||||
)
|
||||
if version < 5:
|
||||
db.engine.execute('ALTER TABLE server ADD COLUMN role text(64)')
|
||||
if version < 6:
|
||||
db.engine.execute("ALTER TABLE server RENAME TO server_old")
|
||||
db.engine.execute("""
|
||||
CREATE TABLE server (
|
||||
id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
servergroup_id INTEGER NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
host VARCHAR(128) NOT NULL,
|
||||
port INTEGER NOT NULL CHECK (port >= 1024 AND port <= 65534),
|
||||
maintenance_db VARCHAR(64) NOT NULL,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
ssl_mode VARCHAR(16) NOT NULL CHECK (
|
||||
ssl_mode IN (
|
||||
'allow', 'prefer', 'require', 'disable', 'verify-ca', 'verify-full'
|
||||
)),
|
||||
comment VARCHAR(1024), password TEXT(64), role text(64),
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(user_id) REFERENCES user (id),
|
||||
FOREIGN KEY(servergroup_id) REFERENCES servergroup (id)
|
||||
)""")
|
||||
db.engine.execute("""
|
||||
INSERT INTO server (
|
||||
id, user_id, servergroup_id, name, host, port, maintenance_db, username,
|
||||
ssl_mode, comment, password, role
|
||||
) SELECT
|
||||
id, user_id, servergroup_id, name, host, port, maintenance_db, username,
|
||||
ssl_mode, comment, password, role
|
||||
FROM server_old""")
|
||||
db.engine.execute("DROP TABLE server_old")
|
||||
|
||||
if version < 8:
|
||||
db.engine.execute("""
|
||||
CREATE TABLE module_preference(
|
||||
id INTEGER PRIMARY KEY,
|
||||
name VARCHAR(256) NOT NULL
|
||||
)""")
|
||||
|
||||
db.engine.execute("""
|
||||
CREATE TABLE preference_category(
|
||||
id INTEGER PRIMARY KEY,
|
||||
mid INTEGER,
|
||||
name VARCHAR(256) NOT NULL,
|
||||
|
||||
FOREIGN KEY(mid) REFERENCES module_preference(id)
|
||||
)""")
|
||||
|
||||
db.engine.execute("""
|
||||
CREATE TABLE preferences (
|
||||
|
||||
id INTEGER PRIMARY KEY,
|
||||
cid INTEGER NOT NULL,
|
||||
name VARCHAR(256) NOT NULL,
|
||||
|
||||
FOREIGN KEY(cid) REFERENCES preference_category (id)
|
||||
)""")
|
||||
|
||||
db.engine.execute("""
|
||||
CREATE TABLE user_preferences (
|
||||
|
||||
pid INTEGER,
|
||||
uid INTEGER,
|
||||
value VARCHAR(1024) NOT NULL,
|
||||
|
||||
PRIMARY KEY (pid, uid),
|
||||
FOREIGN KEY(pid) REFERENCES preferences (pid),
|
||||
FOREIGN KEY(uid) REFERENCES user (id)
|
||||
)""")
|
||||
|
||||
if version < 9:
|
||||
db.engine.execute("""
|
||||
CREATE TABLE IF NOT EXISTS debugger_function_arguments (
|
||||
server_id INTEGER ,
|
||||
database_id INTEGER ,
|
||||
schema_id INTEGER ,
|
||||
function_id INTEGER ,
|
||||
arg_id INTEGER ,
|
||||
is_null INTEGER NOT NULL CHECK (is_null >= 0 AND is_null <= 1) ,
|
||||
is_expression INTEGER NOT NULL CHECK (is_expression >= 0 AND is_expression <= 1) ,
|
||||
use_default INTEGER NOT NULL CHECK (use_default >= 0 AND use_default <= 1) ,
|
||||
value TEXT,
|
||||
PRIMARY KEY (server_id, database_id, schema_id, function_id, arg_id)
|
||||
)""")
|
||||
|
||||
if version < 10:
|
||||
db.engine.execute("""
|
||||
CREATE TABLE process(
|
||||
user_id INTEGER NOT NULL,
|
||||
pid TEXT NOT NULL,
|
||||
desc TEXT NOT NULL,
|
||||
command TEXT NOT NULL,
|
||||
arguments TEXT,
|
||||
start_time TEXT,
|
||||
end_time TEXT,
|
||||
logdir TEXT,
|
||||
exit_code INTEGER,
|
||||
acknowledge TEXT,
|
||||
PRIMARY KEY(pid),
|
||||
FOREIGN KEY(user_id) REFERENCES user (id)
|
||||
)""")
|
||||
|
||||
if version < 11:
|
||||
db.engine.execute("""
|
||||
UPDATE role
|
||||
SET name = 'Administrator',
|
||||
description = 'pgAdmin Administrator Role'
|
||||
WHERE name = 'Administrators'
|
||||
""")
|
||||
|
||||
db.engine.execute("""
|
||||
INSERT INTO role ( name, description )
|
||||
VALUES ('User', 'pgAdmin User Role')
|
||||
""")
|
||||
|
||||
if version < 12:
|
||||
db.engine.execute("ALTER TABLE server RENAME TO server_old")
|
||||
db.engine.execute("""
|
||||
CREATE TABLE server (
|
||||
id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
servergroup_id INTEGER NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
host VARCHAR(128) NOT NULL,
|
||||
port INTEGER NOT NULL CHECK (port >= 1024 AND port <= 65535),
|
||||
maintenance_db VARCHAR(64) NOT NULL,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
ssl_mode VARCHAR(16) NOT NULL CHECK (
|
||||
ssl_mode IN (
|
||||
'allow', 'prefer', 'require', 'disable', 'verify-ca', 'verify-full'
|
||||
)),
|
||||
comment VARCHAR(1024), password TEXT(64), role text(64),
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(user_id) REFERENCES user (id),
|
||||
FOREIGN KEY(servergroup_id) REFERENCES servergroup (id)
|
||||
)""")
|
||||
db.engine.execute("""
|
||||
INSERT INTO server (
|
||||
id, user_id, servergroup_id, name, host, port, maintenance_db, username,
|
||||
ssl_mode, comment, password, role
|
||||
) SELECT
|
||||
id, user_id, servergroup_id, name, host, port, maintenance_db, username,
|
||||
ssl_mode, comment, password, role
|
||||
FROM server_old""")
|
||||
db.engine.execute("DROP TABLE server_old")
|
||||
|
||||
if version < 13:
|
||||
db.engine.execute("""
|
||||
ALTER TABLE SERVER
|
||||
ADD COLUMN discovery_id TEXT
|
||||
""")
|
||||
|
||||
if version < 14:
|
||||
db.engine.execute("""
|
||||
CREATE TABLE keys (
|
||||
name TEST NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (name))
|
||||
""")
|
||||
|
||||
sql = "INSERT INTO keys (name, value) VALUES ('CSRF_SESSION_KEY', '%s')" % base64.urlsafe_b64encode(
|
||||
os.urandom(32)).decode()
|
||||
db.engine.execute(sql)
|
||||
|
||||
sql = "INSERT INTO keys (name, value) VALUES ('SECRET_KEY', '%s')" % base64.urlsafe_b64encode(
|
||||
os.urandom(32)).decode()
|
||||
db.engine.execute(sql)
|
||||
|
||||
# If SECURITY_PASSWORD_SALT is not in the config, but we're upgrading, then it must (unless the
|
||||
# user edited the main config - which they shouldn't have done) have been at it's default
|
||||
# value, so we'll use that. Otherwise, use whatever we can find in the config.
|
||||
if hasattr(config, 'SECURITY_PASSWORD_SALT'):
|
||||
sql = "INSERT INTO keys (name, value) VALUES ('SECURITY_PASSWORD_SALT', '%s')" % config.SECURITY_PASSWORD_SALT
|
||||
else:
|
||||
sql = "INSERT INTO keys (name, value) VALUES ('SECURITY_PASSWORD_SALT', 'SuperSecret3')"
|
||||
db.engine.execute(sql)
|
||||
|
||||
# Finally, update the schema version
|
||||
|
||||
# version.value = config.SETTINGS_SCHEMA_VERSION
|
||||
|
||||
db.engine.execute(
|
||||
'UPDATE version set value="%s" WHERE name = "ConfigDB"' % config.SETTINGS_SCHEMA_VERSION
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
print(u"""
|
||||
Cannot downgrade from this version
|
||||
Exiting...""")
|
||||
sys.exit(1)
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
"""empty message
|
||||
|
||||
Revision ID: 3c1e4b6eda55
|
||||
Revises: 09d53fca90c7
|
||||
Create Date: 2017-06-13 17:05:30.671859
|
||||
|
||||
"""
|
||||
|
||||
from pgadmin.model import db
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3c1e4b6eda55'
|
||||
down_revision = '09d53fca90c7'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN hostaddr TEXT(1024)'
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
"""empty message
|
||||
|
||||
Revision ID: d85a62333272
|
||||
Revises: 3c1e4b6eda55
|
||||
Create Date: 2017-07-07 16:03:23.842734
|
||||
|
||||
"""
|
||||
from pgadmin.model import db
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd85a62333272'
|
||||
down_revision = 'f195f9a4923d'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN db_res TEXT'
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
"""empty message
|
||||
|
||||
Revision ID: ef590e979b0d
|
||||
Revises: d85a62333272
|
||||
Create Date: 2017-08-23 18:37:14.836988
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from pgadmin.model import db
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ef590e979b0d'
|
||||
down_revision = 'd85a62333272'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN passfile TEXT'
|
||||
)
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN sslcert TEXT'
|
||||
)
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN sslkey TEXT'
|
||||
)
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN sslrootcert TEXT'
|
||||
)
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN sslcrl TEXT'
|
||||
)
|
||||
db.engine.execute(
|
||||
'ALTER TABLE server ADD COLUMN sslcompression '
|
||||
'INTEGER default 0'
|
||||
)
|
||||
db.engine.execute(
|
||||
'UPDATE server SET sslcompression=0'
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
"""Encrypt the existing user password.
|
||||
|
||||
Revision ID: f195f9a4923d
|
||||
Revises: 3c1e4b6eda55
|
||||
Create Date: 2017-06-15 17:18:50.667139
|
||||
|
||||
"""
|
||||
import config
|
||||
from flask import current_app
|
||||
from flask_security import Security, SQLAlchemyUserDatastore
|
||||
from flask_security.utils import verify_and_update_password
|
||||
from pgadmin.model import Keys, db, User, Role
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f195f9a4923d'
|
||||
down_revision = '3c1e4b6eda55'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
app = current_app
|
||||
db.session.flush()
|
||||
current_salt = Keys.query.filter_by(name = 'SECURITY_PASSWORD_SALT').first().value
|
||||
app.config.update(dict(SECURITY_PASSWORD_SALT=current_salt))
|
||||
app.config['SECURITY_PASSWORD_HASH'] = config.SECURITY_PASSWORD_HASH
|
||||
|
||||
if app.extensions.get('security') is None:
|
||||
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
||||
Security(app, user_datastore, register_blueprint=False)
|
||||
else:
|
||||
app.config['SECURITY_PASSWORD_SALT'] = current_salt
|
||||
users = User.query.all()
|
||||
# This will upgrade the plaintext password of all the user as per the
|
||||
# SECURITY_PASSWORD_HASH.
|
||||
for user in users:
|
||||
if user.password is not None:
|
||||
verify_and_update_password(user.password, user)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -0,0 +1,149 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""Initial database creation
|
||||
|
||||
Revision ID: fdc58d9bd449
|
||||
Revises:
|
||||
Create Date: 2017-03-13 11:15:16.401139
|
||||
|
||||
"""
|
||||
import base64
|
||||
import os
|
||||
|
||||
import config
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from flask import current_app
|
||||
from flask_security import Security, SQLAlchemyUserDatastore
|
||||
from flask_security.utils import encrypt_password
|
||||
from pgadmin.model import db, User, Role
|
||||
from pgadmin.setup import get_version
|
||||
from pgadmin.setup import user_info
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fdc58d9bd449'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
if get_version() != -1:
|
||||
return
|
||||
|
||||
op.create_table('version',
|
||||
sa.Column('name', sa.String(length=32), nullable=False),
|
||||
sa.Column('value', sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('name')
|
||||
)
|
||||
op.create_table('user',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('email', sa.String(length=256), nullable=False),
|
||||
sa.Column('password', sa.String(length=256), nullable=True),
|
||||
sa.Column('active', sa.Boolean(), nullable=False),
|
||||
sa.Column('confirmed_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('email')
|
||||
)
|
||||
op.create_table('role',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=128), nullable=False),
|
||||
sa.Column('description', sa.String(length=256), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('setting',
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('setting', sa.String(length=256), nullable=False),
|
||||
sa.Column('value', sa.String(length=1024), nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('user_id', 'setting')
|
||||
)
|
||||
op.create_table('roles_users',
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['role.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], )
|
||||
)
|
||||
op.create_table('servergroup',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=128), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('user_id', 'name')
|
||||
)
|
||||
op.create_table('server',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('servergroup_id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=128), nullable=False),
|
||||
sa.Column('host', sa.String(length=128), nullable=False),
|
||||
sa.Column('port', sa.Integer(), nullable=False),
|
||||
sa.Column('maintenance_db', sa.String(length=64), nullable=False),
|
||||
sa.Column('username', sa.String(length=64), nullable=False),
|
||||
sa.Column('ssl_mode', sa.String(length=16), nullable=False),
|
||||
sa.ForeignKeyConstraint(['servergroup_id'], ['servergroup.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
db.engine.execute("""
|
||||
INSERT INTO "role"
|
||||
VALUES(1, 'Administrators', 'pgAdmin Administrators Role')
|
||||
""")
|
||||
db.engine.execute("""
|
||||
INSERT INTO "roles_users"
|
||||
VALUES(1, 1);
|
||||
""")
|
||||
db.engine.execute("""
|
||||
INSERT INTO "servergroup"
|
||||
VALUES(1, 1, 'Servers')
|
||||
""")
|
||||
|
||||
email, password = user_info()
|
||||
current_salt = getattr(
|
||||
config, 'SECURITY_PASSWORD_SALT', base64.urlsafe_b64encode(
|
||||
os.urandom(32)
|
||||
).decode()
|
||||
)
|
||||
if current_app.extensions.get('security') is None:
|
||||
current_app.config['SECURITY_PASSWORD_SALT'] = current_salt
|
||||
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
||||
Security(current_app, user_datastore, register_blueprint=False)
|
||||
else:
|
||||
current_app.config['SECURITY_PASSWORD_SALT'] = current_salt
|
||||
setattr(config, 'SECURITY_PASSWORD_SALT', current_salt)
|
||||
password = encrypt_password(password)
|
||||
|
||||
db.engine.execute("""
|
||||
INSERT INTO "user"
|
||||
VALUES(1, '%s',
|
||||
'%s',
|
||||
1, NULL)
|
||||
""" % (email, password))
|
||||
db.engine.execute("""
|
||||
INSERT INTO "version"
|
||||
VALUES('ConfigDB', 2);
|
||||
""")
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('server')
|
||||
op.drop_table('servergroup')
|
||||
op.drop_table('roles_users')
|
||||
op.drop_table('setting')
|
||||
op.drop_table('role')
|
||||
op.drop_table('user')
|
||||
op.drop_table('version')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,10 +1,98 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"jasmine-core": "^2.5.2",
|
||||
"karma": "^1.5.0",
|
||||
"karma-jasmine": "^1.1.0",
|
||||
"karma-phantomjs-launcher": "^1.0.2",
|
||||
"karma-requirejs": "^1.1.0",
|
||||
"requirejs": "^2.3.3"
|
||||
"babel-core": "~6.24.0",
|
||||
"babel-loader": "~6.4.1",
|
||||
"babel-preset-es2015": "~6.24.0",
|
||||
"babel-preset-react": "~6.23.0",
|
||||
"cross-env": "^5.0.1",
|
||||
"eclint": "^2.3.0",
|
||||
"enzyme": "~2.8.2",
|
||||
"enzyme-matchers": "^3.1.0",
|
||||
"eslint": "3.19.0",
|
||||
"eslint-plugin-react": "^6.10.3",
|
||||
"extract-text-webpack-plugin": "^2.1.2",
|
||||
"file-loader": "^0.11.2",
|
||||
"image-webpack-loader": "^3.3.1",
|
||||
"jasmine-core": "~2.5.2",
|
||||
"jasmine-enzyme": "^3.1.0",
|
||||
"karma": "~1.5.0",
|
||||
"karma-babel-preprocessor": "^6.0.1",
|
||||
"karma-browserify": "~5.1.1",
|
||||
"karma-jasmine": "~1.1.0",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"karma-phantomjs-launcher": "~1.0.2",
|
||||
"karma-requirejs": "~1.1.0",
|
||||
"karma-sourcemap-loader": "~0.3.7",
|
||||
"karma-webpack": "~2.0.3",
|
||||
"node-sass": "^4.5.3",
|
||||
"optimize-css-assets-webpack-plugin": "^2.0.0",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react-addons-test-utils": "~15.4.2",
|
||||
"sass-loader": "^6.0.6",
|
||||
"style-loader": "^0.18.2",
|
||||
"uglifyjs-webpack-plugin": "^0.4.6",
|
||||
"url-loader": "^0.5.9",
|
||||
"webpack": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acitree": "git+https://github.com/imsurinder90/jquery-aciTree.git#rc.7",
|
||||
"alertifyjs": "^1.10.0",
|
||||
"axios": "^0.16.1",
|
||||
"babel-plugin-transform-es2015-modules-amd": "^6.24.1",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-preset-es2015-without-strict": "~0.0.4",
|
||||
"babelify": "~7.3.0",
|
||||
"backbone": "1.1.2",
|
||||
"backform": "^0.2.0",
|
||||
"backgrid": "^0.3.8",
|
||||
"backgrid-filter": "^0.3.7",
|
||||
"backgrid-select-all": "^0.3.5",
|
||||
"backgrid-sizeable-columns": "^0.1.1",
|
||||
"bignumber.js": "^4.0.2",
|
||||
"bootstrap": "^3.3.7",
|
||||
"bootstrap-datepicker": "^1.7.0",
|
||||
"bootstrap-switch": "3.3.2",
|
||||
"bowser": "1.6.1",
|
||||
"browserify": "~14.1.0",
|
||||
"codemirror": "^5.29.0",
|
||||
"css-loader": "0.14.0",
|
||||
"cssnano": "^3.10.0",
|
||||
"dropzone": "^5.1.1",
|
||||
"eonasdan-bootstrap-datetimepicker": "^4.17.47",
|
||||
"exports-loader": "~0.6.4",
|
||||
"flotr2": "^0.1.0",
|
||||
"font-awesome": "^4.7.0",
|
||||
"hard-source-webpack-plugin": "^0.4.9",
|
||||
"immutability-helper": "^2.2.0",
|
||||
"imports-loader": "^0.7.1",
|
||||
"jquery": "1.11.2",
|
||||
"jquery-contextmenu": "^2.5.0",
|
||||
"jquery-ui": "^1.12.1",
|
||||
"moment": "^2.18.1",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "file:../web/pgadmin/static/vendor/react",
|
||||
"react-dom": "file:../web/pgadmin/static/vendor/react-dom",
|
||||
"react-split-pane": "^0.1.63",
|
||||
"requirejs": "~2.3.3",
|
||||
"select2": "^4.0.3",
|
||||
"shim-loader": "^1.0.1",
|
||||
"slickgrid": "git+https://github.com/6pac/SlickGrid.git#2.3.7",
|
||||
"snapsvg": "^0.5.1",
|
||||
"underscore": "^1.8.3",
|
||||
"underscore.string": "^3.3.4",
|
||||
"watchify": "~3.9.0",
|
||||
"webcabin-docker": "^2.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"linter": "yarn run eslint pgadmin/static/jsx/**/*.jsx pgadmin/static/js/selection/*.js regression/javascript/**/*.jsx regression/javascript/**/*.js *.js",
|
||||
"webpacker": "yarn run webpack -- --config webpack.config.js --progress",
|
||||
"webpacker:watch": "yarn run webpack -- --config webpack.config.js --progress --watch",
|
||||
"bundle:watch": "yarn run linter && yarn run webpacker:watch",
|
||||
"bundle:dev": "yarn run linter && yarn run webpacker",
|
||||
"bundle": "cross-env NODE_ENV=production yarn run bundle:dev",
|
||||
"test:karma-once": "yarn run linter && yarn run karma start -- --single-run",
|
||||
"test:karma": "yarn run linter && yarn run karma start",
|
||||
"test:feature": "yarn run bundle && python regression/runtests.py --pkg feature_tests",
|
||||
"test": "yarn run test:karma-once && yarn run bundle && python regression/runtests.py"
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,29 @@ to start a web server."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
import builtins
|
||||
else:
|
||||
import __builtin__ as builtins
|
||||
|
||||
# We need to include the root directory in sys.path to ensure that we can
|
||||
# find everything we need when running in the standalone runtime.
|
||||
root = os.path.dirname(os.path.realpath(__file__))
|
||||
if sys.path[0] != root:
|
||||
sys.path.insert(0, root)
|
||||
|
||||
# Grab the SERVER_MODE if it's been set by the runtime
|
||||
if 'SERVER_MODE' in globals():
|
||||
builtins.SERVER_MODE = globals()['SERVER_MODE']
|
||||
else:
|
||||
builtins.SERVER_MODE = None
|
||||
|
||||
import config
|
||||
from pgadmin import create_app
|
||||
from pgadmin.utils import u, fs_encoding, file_quote
|
||||
|
||||
if config.DEBUG:
|
||||
from pgadmin.utils.javascript.javascript_bundler import JavascriptBundler, JsState
|
||||
|
||||
# Get the config database schema version. We store this in pgadmin.model
|
||||
# as it turns out that putting it in the config files isn't a great idea
|
||||
@@ -34,16 +49,20 @@ config.SETTINGS_SCHEMA_VERSION = SCHEMA_VERSION
|
||||
|
||||
# Check if the database exists. If it does not, create it.
|
||||
if not os.path.isfile(config.SQLITE_PATH):
|
||||
from pgadmin.utils import u, fs_encoding, file_quote
|
||||
setupfile = os.path.join(
|
||||
os.path.dirname(os.path.realpath(u(__file__, fs_encoding))), u'setup.py'
|
||||
)
|
||||
exec(open(file_quote(setupfile), 'r').read())
|
||||
|
||||
##########################################################################
|
||||
# Server starup
|
||||
# Server startup
|
||||
##########################################################################
|
||||
|
||||
# Build Javascript files
|
||||
if config.DEBUG:
|
||||
javascriptBundler = JavascriptBundler()
|
||||
javascriptBundler.bundle()
|
||||
|
||||
# Create the app!
|
||||
app = create_app()
|
||||
|
||||
@@ -52,6 +71,13 @@ if config.DEBUG:
|
||||
else:
|
||||
app.debug = False
|
||||
|
||||
# respond to JS
|
||||
if config.DEBUG:
|
||||
if javascriptBundler.report() == JsState.NONE:
|
||||
app.logger.error("Unable to generate javascript")
|
||||
app.logger.error("To run the app ensure that yarn install command runs successfully")
|
||||
raise Exception("No generated javascript, aborting")
|
||||
|
||||
# Start the web server. The port number should have already been set by the
|
||||
# runtime if we're running in desktop mode, otherwise we'll just use the
|
||||
# Flask default.
|
||||
@@ -104,11 +130,19 @@ if __name__ == '__main__':
|
||||
if IS_WIN:
|
||||
os.environ['PYTHONHOME'] = sys.prefix
|
||||
|
||||
# Initialize Flask service only once
|
||||
# If `WERKZEUG_RUN_MAIN` is None, i.e: app is initializing for first time
|
||||
# so set `use_reloader` = False, thus reload won't call.
|
||||
# Reference:
|
||||
# https://github.com/pallets/werkzeug/issues/220#issuecomment-11176538
|
||||
try:
|
||||
app.run(
|
||||
host=config.DEFAULT_SERVER,
|
||||
port=server_port,
|
||||
use_reloader=((not PGADMIN_RUNTIME) and app.debug),
|
||||
use_reloader=(
|
||||
(not PGADMIN_RUNTIME) and app.debug
|
||||
and os.environ.get("WERKZEUG_RUN_MAIN") is not None
|
||||
),
|
||||
threaded=config.THREADED_MODE
|
||||
)
|
||||
except IOError:
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -5,4 +14,23 @@ root = os.path.dirname(os.path.realpath(__file__))
|
||||
if sys.path[0] != root:
|
||||
sys.path.insert(0, root)
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
import builtins
|
||||
else:
|
||||
import __builtin__ as builtins
|
||||
|
||||
# Ensure the global server mode is set.
|
||||
builtins.SERVER_MODE = True
|
||||
|
||||
import config
|
||||
|
||||
# When running it as a WSGI application, directory for the configuration file
|
||||
# must present.
|
||||
if not os.path.exists(os.path.dirname(config.SQLITE_PATH)):
|
||||
raise Exception(
|
||||
"""
|
||||
Required configuration file is not present!
|
||||
Please run setup.py first!"""
|
||||
)
|
||||
|
||||
from pgAdmin4 import app as application
|
||||
@@ -10,18 +10,19 @@
|
||||
"""The main pgAdmin module. This handles the application initialisation tasks,
|
||||
such as setup of logging, dynamic loading of modules etc."""
|
||||
import logging
|
||||
import os, sys, time
|
||||
import os, sys
|
||||
from collections import defaultdict
|
||||
from importlib import import_module
|
||||
|
||||
from flask import Flask, abort, request, current_app, session
|
||||
from flask import Flask, abort, request, current_app, session, url_for
|
||||
from flask_babel import Babel, gettext
|
||||
from flask_htmlmin import HTMLMIN
|
||||
from flask_login import user_logged_in
|
||||
from flask_login import user_logged_in, user_logged_out
|
||||
from flask_security import Security, SQLAlchemyUserDatastore
|
||||
from flask_mail import Mail
|
||||
from flask_security.utils import login_user
|
||||
from werkzeug.datastructures import ImmutableDict
|
||||
from flask_paranoid import Paranoid
|
||||
|
||||
from pgadmin.utils import PgAdminModule, driver
|
||||
from pgadmin.utils.versioned_template_loader import VersionedTemplateLoader
|
||||
@@ -31,7 +32,9 @@ from werkzeug.utils import find_modules
|
||||
|
||||
from pgadmin.utils.preferences import Preferences
|
||||
|
||||
from pgadmin.model import db, Role, Server, ServerGroup, User, Version, Keys
|
||||
from pgadmin.model import db, Role, Server, ServerGroup, \
|
||||
User, Keys, Version, SCHEMA_VERSION as CURRENT_SCHEMA_VERSION
|
||||
|
||||
|
||||
# If script is running under python3, it will not have the xrange function
|
||||
# defined
|
||||
@@ -86,6 +89,42 @@ class PgAdmin(Flask):
|
||||
messages.update(getattr(module, "messages", dict()))
|
||||
return messages
|
||||
|
||||
@property
|
||||
def exposed_endpoint_url_map(self):
|
||||
#############################################################
|
||||
# To handle WSGI paths
|
||||
# If user has setup application under WSGI alias
|
||||
# like 'localhost/pgadmin4' then we have to append '/pgadmin4'
|
||||
# into endpoints
|
||||
#############################################################
|
||||
import config
|
||||
is_wsgi_root_present = False
|
||||
if config.SERVER_MODE:
|
||||
pgadmin_root_path = url_for('browser.index')
|
||||
if pgadmin_root_path != '/browser/':
|
||||
is_wsgi_root_present = True
|
||||
wsgi_root_path = pgadmin_root_path.replace(
|
||||
'/browser/', ''
|
||||
)
|
||||
|
||||
def get_full_url_path(url):
|
||||
"""
|
||||
Generate endpoint URL at per WSGI alias
|
||||
"""
|
||||
if is_wsgi_root_present and url:
|
||||
return wsgi_root_path + url
|
||||
else:
|
||||
return url
|
||||
|
||||
# Fetch all endpoints and their respective url
|
||||
for rule in current_app.url_map.iter_rules('static'):
|
||||
yield rule.endpoint, get_full_url_path(rule.rule)
|
||||
|
||||
for module in self.submodules:
|
||||
for endpoint in module.exposed_endpoints:
|
||||
for rule in current_app.url_map.iter_rules(endpoint):
|
||||
yield rule.endpoint, get_full_url_path(rule.rule)
|
||||
|
||||
@property
|
||||
def javascripts(self):
|
||||
scripts = []
|
||||
@@ -160,6 +199,15 @@ def create_app(app_name=None):
|
||||
logger = logging.getLogger('werkzeug')
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# Set SQLITE_PATH to TEST_SQLITE_PATH while running test cases
|
||||
if "PGADMIN_TESTING_MODE" in os. environ and \
|
||||
os.environ["PGADMIN_TESTING_MODE"] == "1":
|
||||
config.SQLITE_PATH = config.TEST_SQLITE_PATH
|
||||
|
||||
# Ensure the various working directories exist
|
||||
from pgadmin.setup import create_app_data_directory, db_upgrade
|
||||
create_app_data_directory(config)
|
||||
|
||||
# File logging
|
||||
fh = logging.FileHandler(config.LOG_FILE, encoding='utf-8')
|
||||
fh.setLevel(config.FILE_LOG_LEVEL)
|
||||
@@ -233,6 +281,30 @@ def create_app(app_name=None):
|
||||
|
||||
# Create database connection object and mailer
|
||||
db.init_app(app)
|
||||
|
||||
##########################################################################
|
||||
# Upgrade the schema (if required)
|
||||
##########################################################################
|
||||
with app.app_context():
|
||||
# Run migration for the first time i.e. create database
|
||||
from config import SQLITE_PATH
|
||||
if not os.path.exists(SQLITE_PATH):
|
||||
db_upgrade(app)
|
||||
else:
|
||||
version = Version.query.filter_by(name='ConfigDB').first()
|
||||
schema_version = version.value
|
||||
|
||||
# Run migration if current schema version is greater than the
|
||||
# schema version stored in version table
|
||||
if CURRENT_SCHEMA_VERSION >= schema_version:
|
||||
db_upgrade(app)
|
||||
|
||||
# Update schema version to the latest
|
||||
if CURRENT_SCHEMA_VERSION > schema_version:
|
||||
version = Version.query.filter_by(name='ConfigDB').first()
|
||||
version.value = CURRENT_SCHEMA_VERSION
|
||||
db.session.commit()
|
||||
|
||||
Mail(app)
|
||||
|
||||
import pgadmin.utils.paths as paths
|
||||
@@ -242,33 +314,6 @@ def create_app(app_name=None):
|
||||
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
||||
security = Security(None, user_datastore)
|
||||
|
||||
# Upgrade the schema (if required)
|
||||
with app.app_context():
|
||||
try:
|
||||
version = Version.query.filter_by(name='ConfigDB').first()
|
||||
except:
|
||||
backup_file = config.SQLITE_PATH + '.' + time.strftime("%Y%m%d%H%M%S")
|
||||
app.logger.error(
|
||||
"""The configuration database ({0}) appears to be corrupt.\n\n"""
|
||||
"""The database will be moved to {1}.\n"""
|
||||
"""Please restart {2} to create a new configuration database.\n""".format(
|
||||
config.SQLITE_PATH, backup_file, config.APP_NAME
|
||||
)
|
||||
)
|
||||
|
||||
os.rename(config.SQLITE_PATH, backup_file)
|
||||
exit(1)
|
||||
|
||||
# Pre-flight checks
|
||||
if int(version.value) < int(config.SETTINGS_SCHEMA_VERSION):
|
||||
app.logger.info(
|
||||
"""Upgrading the database schema from version {0} to {1}.""".format(
|
||||
version.value, config.SETTINGS_SCHEMA_VERSION
|
||||
)
|
||||
)
|
||||
from setup import do_upgrade
|
||||
do_upgrade(app, user_datastore, version)
|
||||
|
||||
##########################################################################
|
||||
# Setup security
|
||||
##########################################################################
|
||||
@@ -283,10 +328,15 @@ def create_app(app_name=None):
|
||||
app.config.update(dict(SECRET_KEY=config.SECRET_KEY))
|
||||
app.config.update(dict(SECURITY_PASSWORD_SALT=config.SECURITY_PASSWORD_SALT))
|
||||
|
||||
security.init_app(app)
|
||||
security.init_app(app, user_datastore)
|
||||
|
||||
app.session_interface = create_session_interface(app)
|
||||
|
||||
# Make the Session more secure against XSS & CSRF when running in web mode
|
||||
if config.SERVER_MODE:
|
||||
paranoid = Paranoid(app)
|
||||
paranoid.redirect_view = 'browser.index'
|
||||
|
||||
##########################################################################
|
||||
# Load all available server drivers
|
||||
##########################################################################
|
||||
@@ -318,7 +368,6 @@ def create_app(app_name=None):
|
||||
##########################################################################
|
||||
@user_logged_in.connect_via(app)
|
||||
def on_user_logged_in(sender, user):
|
||||
|
||||
# Keep hold of the user ID
|
||||
user_id = user.id
|
||||
|
||||
@@ -447,6 +496,10 @@ def create_app(app_name=None):
|
||||
except:
|
||||
pass
|
||||
|
||||
@user_logged_in.connect_via(app)
|
||||
@user_logged_out.connect_via(app)
|
||||
def force_session_write(app, user):
|
||||
session.force_write = True
|
||||
|
||||
##########################################################################
|
||||
# Load plugin modules
|
||||
@@ -513,6 +566,5 @@ def create_app(app_name=None):
|
||||
##########################################################################
|
||||
# All done!
|
||||
##########################################################################
|
||||
app.logger.debug('URL map: %s' % app.url_map)
|
||||
|
||||
return app
|
||||
@@ -47,6 +47,8 @@ class AboutModule(PgAdminModule):
|
||||
'when': None
|
||||
}]
|
||||
|
||||
def get_exposed_url_endpoints(self):
|
||||
return ['about.index']
|
||||
|
||||
blueprint = AboutModule(MODULE_NAME, __name__,
|
||||
static_url_path='')
|
||||
@@ -55,7 +57,7 @@ blueprint = AboutModule(MODULE_NAME, __name__,
|
||||
##########################################################################
|
||||
# A test page
|
||||
##########################################################################
|
||||
@blueprint.route("/")
|
||||
@blueprint.route("/", endpoint='index')
|
||||
@login_required
|
||||
def index():
|
||||
"""Render the about box."""
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
define(
|
||||
['jquery', 'alertify', 'pgadmin', 'sources/gettext'],
|
||||
function($, alertify, pgAdmin, gettext) {
|
||||
['jquery', 'alertify', 'sources/pgadmin', 'underscore.string', 'sources/gettext',
|
||||
'sources/url_for'
|
||||
],
|
||||
function($, alertify, pgAdmin, S, gettext, url_for) {
|
||||
pgAdmin = pgAdmin || window.pgAdmin || {};
|
||||
|
||||
/* Return back, this has been called more than once */
|
||||
@@ -39,9 +41,11 @@ define(
|
||||
}
|
||||
|
||||
var content = '';
|
||||
$.get("{{ url_for('about.index') }}",
|
||||
$.get(url_for('about.index'),
|
||||
function(data) {
|
||||
alertify.aboutDialog(gettext("About %(appname)s", {appname: "{{ config.APP_NAME }}"}), data).resizeTo(800, 450);
|
||||
alertify.aboutDialog(
|
||||
S(gettext("About %s")).sprintf(pgAdmin.Browser.utils.app_name).value(), data
|
||||
).resizeTo(800, 450);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="enable-selection">
|
||||
<div class="container-fluid enable-selection">
|
||||
<div class="row">
|
||||
<div class="col-sm-3"><b>{{ _('Version') }}</b></div>
|
||||
<div class="col-sm-9">{{ config.APP_VERSION }}</div>
|
||||
@@ -23,9 +23,9 @@
|
||||
<div class="col-sm-3"><b>{{ _('Current User') }}</b></div>
|
||||
<div class="col-sm-9">{{ info.current_user }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-3 pull-right"><img
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-right"><img
|
||||
src="{{ url_for('static', filename='img/logo-right-128.png') }}"
|
||||
alt="{{ config.APP_NAME }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -12,7 +12,7 @@ from abc import ABCMeta, abstractmethod, abstractproperty
|
||||
|
||||
import six
|
||||
from flask import current_app, render_template, url_for, make_response, flash,\
|
||||
Response, request
|
||||
Response
|
||||
from flask_babel import gettext
|
||||
from flask_login import current_user
|
||||
from flask_security import login_required
|
||||
@@ -152,6 +152,7 @@ class BrowserModule(PgAdminModule):
|
||||
|
||||
for name, script in [
|
||||
['pgadmin.browser', 'js/browser'],
|
||||
['pgadmin.browser.endpoints', 'js/endpoints'],
|
||||
['pgadmin.browser.error', 'js/error']]:
|
||||
scripts.append({
|
||||
'name': name,
|
||||
@@ -181,7 +182,7 @@ class BrowserModule(PgAdminModule):
|
||||
scripts.append({
|
||||
'name': 'pgadmin.browser.node.ui',
|
||||
'path': url_for('browser.static', filename='js/node.ui'),
|
||||
'when': 'server-group'
|
||||
'when': 'server_group'
|
||||
})
|
||||
|
||||
for module in self.submodules:
|
||||
@@ -195,6 +196,13 @@ class BrowserModule(PgAdminModule):
|
||||
category_label=gettext('Display')
|
||||
)
|
||||
|
||||
def get_exposed_url_endpoints(self):
|
||||
"""
|
||||
Returns:
|
||||
list: a list of url endpoints exposed to the client.
|
||||
"""
|
||||
return ['browser.index', 'browser.nodes']
|
||||
|
||||
blueprint = BrowserModule(MODULE_NAME, __name__)
|
||||
|
||||
|
||||
@@ -251,6 +259,14 @@ class BrowserPluginModule(PgAdminModule):
|
||||
"""
|
||||
return []
|
||||
|
||||
@property
|
||||
def module_use_template_javascript(self):
|
||||
"""
|
||||
Returns whether Jinja2 template is used for generating the javascript
|
||||
module.
|
||||
"""
|
||||
return False
|
||||
|
||||
def get_own_javascripts(self):
|
||||
"""
|
||||
Returns the list of javascripts information used by the module.
|
||||
@@ -272,14 +288,26 @@ class BrowserPluginModule(PgAdminModule):
|
||||
"""
|
||||
scripts = []
|
||||
|
||||
scripts.extend([{
|
||||
'name': 'pgadmin.node.%s' % self.node_type,
|
||||
'path': url_for('browser.index') + '%s/module' % self.node_type,
|
||||
'when': self.script_load
|
||||
}])
|
||||
if self.module_use_template_javascript:
|
||||
scripts.extend([{
|
||||
'name': 'pgadmin.node.%s' % self.node_type,
|
||||
'path': url_for('browser.index') + '%s/module' % self.node_type,
|
||||
'when': self.script_load,
|
||||
'is_template': True
|
||||
}])
|
||||
else:
|
||||
scripts.extend([{
|
||||
'name': 'pgadmin.node.%s' % self.node_type,
|
||||
'path': url_for(
|
||||
'%s.static'% self.name, filename=('js/%s' % self.node_type)
|
||||
),
|
||||
'when': self.script_load,
|
||||
'is_template': False
|
||||
}])
|
||||
|
||||
for module in self.submodules:
|
||||
scripts.extend(module.get_own_javascripts())
|
||||
|
||||
return scripts
|
||||
|
||||
def generate_browser_node(
|
||||
@@ -495,9 +523,9 @@ def index():
|
||||
|
||||
return response
|
||||
|
||||
@blueprint.route("/js/browser.js")
|
||||
@blueprint.route("/js/utils.js")
|
||||
@login_required
|
||||
def browser_js():
|
||||
def utils():
|
||||
layout = get_setting('Browser/Layout', default='')
|
||||
snippets = []
|
||||
|
||||
@@ -521,11 +549,17 @@ def browser_js():
|
||||
editor_wrap_code_pref = prefs.preference('wrap_code')
|
||||
editor_wrap_code = editor_wrap_code_pref.get()
|
||||
|
||||
brace_matching_pref = prefs.preference('brace_matching')
|
||||
brace_matching = brace_matching_pref.get()
|
||||
|
||||
insert_pair_brackets_perf = prefs.preference('insert_pair_brackets')
|
||||
insert_pair_brackets = insert_pair_brackets_perf.get()
|
||||
|
||||
for submodule in current_blueprint.submodules:
|
||||
snippets.extend(submodule.jssnippets)
|
||||
return make_response(
|
||||
render_template(
|
||||
'browser/js/browser.js',
|
||||
'browser/js/utils.js',
|
||||
layout=layout,
|
||||
jssnippets=snippets,
|
||||
pg_help_path=pg_help_path,
|
||||
@@ -533,11 +567,21 @@ def browser_js():
|
||||
editor_tab_size=editor_tab_size,
|
||||
editor_use_spaces=editor_use_spaces,
|
||||
editor_wrap_code=editor_wrap_code,
|
||||
_=gettext
|
||||
editor_brace_matching=brace_matching,
|
||||
editor_insert_pair_brackets=insert_pair_brackets,
|
||||
app_name=config.APP_NAME
|
||||
),
|
||||
200, {'Content-Type': 'application/x-javascript'})
|
||||
|
||||
|
||||
@blueprint.route("/js/endpoints.js")
|
||||
def exposed_urls():
|
||||
return make_response(
|
||||
render_template('browser/js/endpoints.js'),
|
||||
200, {'Content-Type': 'application/x-javascript'}
|
||||
)
|
||||
|
||||
|
||||
@blueprint.route("/js/error.js")
|
||||
@login_required
|
||||
def error_js():
|
||||
@@ -605,11 +649,12 @@ def browser_css():
|
||||
200, {'Content-Type': 'text/css'})
|
||||
|
||||
|
||||
@blueprint.route("/nodes/")
|
||||
@blueprint.route("/nodes/", endpoint="nodes")
|
||||
@login_required
|
||||
def get_nodes():
|
||||
"""Build a list of treeview nodes from the child nodes."""
|
||||
nodes = []
|
||||
for submodule in current_blueprint.submodules:
|
||||
nodes.extend(submodule.get_nodes())
|
||||
|
||||
return make_json_response(data=nodes)
|
||||
@@ -45,14 +45,34 @@ class CollectionNodeModule(PgAdminModule, PGChildModule):
|
||||
"""
|
||||
return []
|
||||
|
||||
@property
|
||||
def module_use_template_javascript(self):
|
||||
"""
|
||||
Returns whether Jinja2 template is used for generating the javascript
|
||||
module.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
def get_own_javascripts(self):
|
||||
scripts = []
|
||||
|
||||
scripts.extend([{
|
||||
'name': 'pgadmin.node.%s' % self.node_type,
|
||||
'path': url_for('browser.index') + '%s/module' % self.node_type,
|
||||
'when': self.script_load
|
||||
}])
|
||||
if self.module_use_template_javascript:
|
||||
scripts.extend([{
|
||||
'name': 'pgadmin.node.%s' % self.node_type,
|
||||
'path': url_for('browser.index') + '%s/module' % self.node_type,
|
||||
'when': self.script_load,
|
||||
'is_template': True
|
||||
}])
|
||||
else:
|
||||
scripts.extend([{
|
||||
'name': 'pgadmin.node.%s' % self.node_type,
|
||||
'path': url_for(
|
||||
'%s.static'% self.name, filename=('js/%s' % self.node_type)
|
||||
),
|
||||
'when': self.script_load,
|
||||
'is_template': False
|
||||
}])
|
||||
|
||||
for module in self.submodules:
|
||||
scripts.extend(module.get_own_javascripts())
|
||||
|
||||
@@ -13,20 +13,20 @@ import simplejson as json
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import six
|
||||
from flask import request, render_template, make_response, jsonify
|
||||
from flask import request, render_template, make_response, jsonify, current_app
|
||||
from flask_babel import gettext
|
||||
from flask_security import current_user
|
||||
from pgadmin.browser import BrowserPluginModule
|
||||
from pgadmin.browser.utils import NodeView
|
||||
from pgadmin.utils.ajax import make_json_response, gone, \
|
||||
make_response as ajax_response
|
||||
make_response as ajax_response, bad_request
|
||||
from pgadmin.utils.menu import MenuItem
|
||||
|
||||
from sqlalchemy import exc
|
||||
from pgadmin.model import db, ServerGroup
|
||||
|
||||
|
||||
class ServerGroupModule(BrowserPluginModule):
|
||||
NODE_TYPE = "server-group"
|
||||
NODE_TYPE = "server_group"
|
||||
|
||||
def get_nodes(self, *arg, **kwargs):
|
||||
"""Return a JSON document listing the server groups for the user"""
|
||||
@@ -87,7 +87,7 @@ class ServerGroupPluginModule(BrowserPluginModule):
|
||||
pass
|
||||
|
||||
|
||||
blueprint = ServerGroupModule(__name__, static_url_path='')
|
||||
blueprint = ServerGroupModule(__name__)
|
||||
|
||||
|
||||
class ServerGroupView(NodeView):
|
||||
@@ -174,6 +174,10 @@ class ServerGroupView(NodeView):
|
||||
if u'name' in data:
|
||||
servergroup.name = data[u'name']
|
||||
db.session.commit()
|
||||
except exc.IntegrityError:
|
||||
return bad_request(gettext(
|
||||
"The specified server group already exists."
|
||||
))
|
||||
except Exception as e:
|
||||
return make_json_response(
|
||||
status=410, success=0, errormsg=e.message
|
||||
@@ -220,18 +224,6 @@ class ServerGroupView(NodeView):
|
||||
)
|
||||
if data[u'name'] != '':
|
||||
try:
|
||||
check_sg = ServerGroup.query.filter_by(
|
||||
user_id=current_user.id,
|
||||
name=data[u'name']).first()
|
||||
|
||||
# Throw error if server group already exists...
|
||||
if check_sg is not None:
|
||||
return make_json_response(
|
||||
status=409,
|
||||
success=0,
|
||||
errormsg=gettext('Server group already exists')
|
||||
)
|
||||
|
||||
sg = ServerGroup(
|
||||
user_id=current_user.id,
|
||||
name=data[u'name'])
|
||||
@@ -248,9 +240,15 @@ class ServerGroupView(NodeView):
|
||||
"icon-%s" % self.node_type,
|
||||
True,
|
||||
self.node_type,
|
||||
can_delete=True # This is user created hence can deleted
|
||||
# This is user created hence can deleted
|
||||
can_delete=True
|
||||
)
|
||||
)
|
||||
except exc.IntegrityError:
|
||||
return bad_request(gettext(
|
||||
"The specified server group already exists."
|
||||
))
|
||||
|
||||
except Exception as e:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
@@ -278,16 +276,6 @@ class ServerGroupView(NodeView):
|
||||
def dependents(self, gid):
|
||||
return make_json_response(status=422)
|
||||
|
||||
def module_js(self, **kwargs):
|
||||
"""
|
||||
This property defines (if javascript) exists for this node.
|
||||
Override this property for your own logic.
|
||||
"""
|
||||
return make_response(
|
||||
render_template("server_groups/server_groups.js"),
|
||||
200, {'Content-Type': 'application/x-javascript'}
|
||||
)
|
||||
|
||||
def nodes(self, gid=None):
|
||||
"""Return a JSON document listing the server groups for the user"""
|
||||
nodes = []
|
||||
@@ -309,7 +297,9 @@ class ServerGroupView(NodeView):
|
||||
group = ServerGroup.query.filter_by(user_id=current_user.id,
|
||||
id=gid).first()
|
||||
if not group:
|
||||
return gone(errormsg="Could not find the server group.")
|
||||
return gone(
|
||||
errormsg=gettext("Could not find the server group.")
|
||||
)
|
||||
|
||||
nodes = self.blueprint.generate_browser_node(
|
||||
"%d" % (group.id), None,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
##########################################################################
|
||||
|
||||
import simplejson as json
|
||||
|
||||
import re
|
||||
import pgadmin.browser.server_groups as sg
|
||||
from flask import render_template, request, make_response, jsonify, \
|
||||
current_app, url_for
|
||||
@@ -25,7 +25,7 @@ import config
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.model import db, Server, ServerGroup, User
|
||||
from pgadmin.utils.driver import get_driver
|
||||
|
||||
from pgadmin.utils import get_storage_directory
|
||||
|
||||
def has_any(data, keys):
|
||||
"""
|
||||
@@ -48,7 +48,7 @@ def recovery_state(connection, postgres_version):
|
||||
recovery_check_sql = render_template("connect/sql/#{0}#/check_recovery.sql".format(postgres_version))
|
||||
|
||||
status, result = connection.execute_dict(recovery_check_sql)
|
||||
if status:
|
||||
if status and 'rows' in result and len(result['rows']) > 0:
|
||||
in_recovery = result['rows'][0]['inrecovery']
|
||||
wal_paused = result['rows'][0]['isreplaypaused']
|
||||
else:
|
||||
@@ -128,24 +128,23 @@ class ServerModule(sg.ServerGroupPluginModule):
|
||||
scripts = []
|
||||
|
||||
scripts.extend([{
|
||||
'name': 'pgadmin.node.server',
|
||||
'path': url_for('browser.index') + '%s/module' % self.node_type,
|
||||
'when': self.script_load
|
||||
},
|
||||
{
|
||||
'name': 'pgadmin.browser.server.privilege',
|
||||
'path': url_for('browser.index') + 'server/static/js/privilege',
|
||||
'when': self.node_type,
|
||||
'deps': ['pgadmin.browser.node.ui']
|
||||
},
|
||||
{
|
||||
'name': 'pgadmin.browser.server.variable',
|
||||
'path': url_for('browser.index') + 'server/static/js/variable',
|
||||
'when': self.node_type
|
||||
}])
|
||||
|
||||
for module in self.submodules:
|
||||
scripts.extend(module.get_own_javascripts())
|
||||
'name': 'pgadmin.browser.server.privilege',
|
||||
'path': url_for('%s.static'% self.name, filename='js/privilege'),
|
||||
'when': self.node_type,
|
||||
'is_template': False,
|
||||
'deps': ['pgadmin.browser.node.ui']
|
||||
}, {
|
||||
'name': 'pgadmin.browser.server.variable',
|
||||
'path': url_for('%s.static'% self.name, filename='js/variable'),
|
||||
'when': self.node_type,
|
||||
'is_template': False
|
||||
},{
|
||||
'name': 'pgadmin.server.supported_servers',
|
||||
'path': url_for('browser.index') + 'server/supported_servers',
|
||||
'is_template': True,
|
||||
'when': self.node_type
|
||||
}])
|
||||
scripts.extend(sg.ServerGroupPluginModule.get_own_javascripts(self))
|
||||
|
||||
return scripts
|
||||
|
||||
@@ -199,7 +198,7 @@ class ServerNode(PGChildNodeView):
|
||||
'dependency': [{'get': 'dependencies'}],
|
||||
'dependent': [{'get': 'dependents'}],
|
||||
'children': [{'get': 'children'}],
|
||||
'module.js': [{}, {}, {'get': 'module_js'}],
|
||||
'supported_servers.js': [{}, {}, {'get': 'supported_servers'}],
|
||||
'reload':
|
||||
[{'get': 'reload_configuration'}],
|
||||
'restore_point':
|
||||
@@ -212,6 +211,70 @@ class ServerNode(PGChildNodeView):
|
||||
'delete': 'pause_wal_replay', 'put': 'resume_wal_replay'
|
||||
}]
|
||||
})
|
||||
EXP_IP4 = "^\s*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\."\
|
||||
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\."\
|
||||
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\."\
|
||||
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\s*$"
|
||||
EXP_IP6 = '^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|'\
|
||||
'(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|'\
|
||||
'2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|'\
|
||||
'(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|'\
|
||||
':((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|'\
|
||||
'(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|'\
|
||||
'2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|'\
|
||||
'(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|'\
|
||||
'[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|'\
|
||||
'((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|'\
|
||||
'(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|'\
|
||||
'1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|'\
|
||||
'((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$'
|
||||
pat4 = re.compile(EXP_IP4)
|
||||
pat6 = re.compile(EXP_IP6)
|
||||
SSL_MODES = ['prefer','require', 'verify-ca', 'verify-full']
|
||||
|
||||
def check_ssl_fields(self, data):
|
||||
"""
|
||||
This function will allow us to check and set defaults for
|
||||
SSL fields
|
||||
|
||||
Args:
|
||||
data: Response data
|
||||
|
||||
Returns:
|
||||
Flag and Data
|
||||
"""
|
||||
flag = False
|
||||
|
||||
if 'sslmode' in data and data['sslmode'] in self.SSL_MODES:
|
||||
flag = True
|
||||
ssl_fields = [
|
||||
'sslcert', 'sslkey', 'sslrootcert', 'sslcrl', 'sslcompression'
|
||||
]
|
||||
# Required SSL fields for SERVER mode from user
|
||||
required_ssl_fields_server_mode = ['sslcert', 'sslkey']
|
||||
|
||||
for field in ssl_fields:
|
||||
if field not in data:
|
||||
# In Server mode,
|
||||
# we will set dummy SSL certificate file path which will
|
||||
# prevent using default SSL certificates from web servers
|
||||
|
||||
if config.SERVER_MODE and \
|
||||
field in required_ssl_fields_server_mode:
|
||||
# Set file manager directory from preference
|
||||
import os
|
||||
storage_dir = get_storage_directory()
|
||||
file_extn = '.key' if field.endswith('key') else '.crt'
|
||||
dummy_ssl_file = os.path.join(
|
||||
storage_dir, '.postgresql',
|
||||
'postgresql' + file_extn
|
||||
)
|
||||
data[field] = dummy_ssl_file
|
||||
# For Desktop mode, we will allow to default
|
||||
else:
|
||||
data[field] = None
|
||||
|
||||
return flag, data
|
||||
|
||||
def nodes(self, gid):
|
||||
res = []
|
||||
@@ -354,13 +417,21 @@ class ServerNode(PGChildNodeView):
|
||||
config_param_map = {
|
||||
'name': 'name',
|
||||
'host': 'host',
|
||||
'hostaddr': 'hostaddr',
|
||||
'port': 'port',
|
||||
'db': 'maintenance_db',
|
||||
'username': 'username',
|
||||
'sslmode': 'ssl_mode',
|
||||
'gid': 'servergroup_id',
|
||||
'comment': 'comment',
|
||||
'role': 'role'
|
||||
'role': 'role',
|
||||
'db_res': 'db_res',
|
||||
'passfile': 'passfile',
|
||||
'sslcert': 'sslcert',
|
||||
'sslkey': 'sslkey',
|
||||
'sslrootcert': 'sslrootcert',
|
||||
'sslcrl': 'sslcrl',
|
||||
'sslcompression': 'sslcompression'
|
||||
}
|
||||
|
||||
disp_lbl = {
|
||||
@@ -378,6 +449,17 @@ class ServerNode(PGChildNodeView):
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
if 'db_res' in data:
|
||||
data['db_res'] = ','.join(data['db_res'])
|
||||
|
||||
if 'hostaddr' in data and data['hostaddr'] != '':
|
||||
if not self.pat4.match(data['hostaddr']):
|
||||
if not self.pat6.match(data['hostaddr']):
|
||||
return make_json_response(
|
||||
success=0,
|
||||
status=400,
|
||||
errormsg=gettext('Host address not valid')
|
||||
)
|
||||
|
||||
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
|
||||
conn = manager.connection()
|
||||
@@ -385,7 +467,7 @@ class ServerNode(PGChildNodeView):
|
||||
|
||||
if connected:
|
||||
for arg in (
|
||||
'host', 'port', 'db', 'username', 'sslmode', 'role'
|
||||
'host', 'hostaddr', 'port', 'db', 'username', 'sslmode', 'role'
|
||||
):
|
||||
if arg in data:
|
||||
return forbidden(
|
||||
@@ -396,7 +478,12 @@ class ServerNode(PGChildNodeView):
|
||||
|
||||
for arg in config_param_map:
|
||||
if arg in data:
|
||||
setattr(server, config_param_map[arg], data[arg])
|
||||
value = data[arg]
|
||||
# sqlite3 do not have boolean type so we need to convert
|
||||
# it manually to integer
|
||||
if arg == 'sslcompression':
|
||||
value = 1 if value else 0
|
||||
setattr(server, config_param_map[arg], value)
|
||||
idx += 1
|
||||
|
||||
if idx == 0:
|
||||
@@ -466,7 +553,8 @@ class ServerNode(PGChildNodeView):
|
||||
'role': server.role,
|
||||
'connected': connected,
|
||||
'version': manager.ver,
|
||||
'server_type': manager.server_type if connected else 'pg'
|
||||
'server_type': manager.server_type if connected else 'pg',
|
||||
'db_res': server.db_res.split(',') if server.db_res else None
|
||||
})
|
||||
|
||||
return ajax_response(
|
||||
@@ -497,11 +585,14 @@ class ServerNode(PGChildNodeView):
|
||||
conn = manager.connection()
|
||||
connected = conn.connected()
|
||||
|
||||
is_ssl = True if server.ssl_mode in self.SSL_MODES else False
|
||||
|
||||
return ajax_response(
|
||||
response={
|
||||
'id': server.id,
|
||||
'name': server.name,
|
||||
'host': server.host,
|
||||
'hostaddr': server.hostaddr,
|
||||
'port': server.port,
|
||||
'db': server.maintenance_db,
|
||||
'username': server.username,
|
||||
@@ -512,7 +603,15 @@ class ServerNode(PGChildNodeView):
|
||||
'connected': connected,
|
||||
'version': manager.ver,
|
||||
'sslmode': server.ssl_mode,
|
||||
'server_type': manager.server_type if connected else 'pg'
|
||||
'server_type': manager.server_type if connected else 'pg',
|
||||
'db_res': server.db_res.split(',') if server.db_res else None,
|
||||
'passfile': server.passfile if server.passfile else None,
|
||||
'sslcert': server.sslcert if is_ssl else None,
|
||||
'sslkey': server.sslkey if is_ssl else None,
|
||||
'sslrootcert': server.sslrootcert if is_ssl else None,
|
||||
'sslcrl': server.sslcrl if is_ssl else None,
|
||||
'sslcompression': True if is_ssl and server.sslcompression
|
||||
else False
|
||||
}
|
||||
)
|
||||
|
||||
@@ -542,6 +641,18 @@ class ServerNode(PGChildNodeView):
|
||||
)
|
||||
)
|
||||
|
||||
if 'hostaddr' in data and data['hostaddr'] != '':
|
||||
if not self.pat4.match(data['hostaddr']):
|
||||
if not self.pat6.match(data['hostaddr']):
|
||||
return make_json_response(
|
||||
success=0,
|
||||
status=400,
|
||||
errormsg=gettext('Host address not valid')
|
||||
)
|
||||
|
||||
# To check ssl configuration
|
||||
is_ssl, data = self.check_ssl_fields(data)
|
||||
|
||||
server = None
|
||||
|
||||
try:
|
||||
@@ -550,18 +661,24 @@ class ServerNode(PGChildNodeView):
|
||||
servergroup_id=data[u'gid'] if u'gid' in data else gid,
|
||||
name=data[u'name'],
|
||||
host=data[u'host'],
|
||||
hostaddr=data[u'hostaddr'] if u'hostaddr' in data else None,
|
||||
port=data[u'port'],
|
||||
maintenance_db=data[u'db'],
|
||||
username=data[u'username'],
|
||||
ssl_mode=data[u'sslmode'],
|
||||
comment=data[u'comment'] if u'comment' in data else None,
|
||||
role=data[u'role'] if u'role' in data else None
|
||||
role=data[u'role'] if u'role' in data else None,
|
||||
db_res=','.join(data[u'db_res']) if u'db_res' in data else None,
|
||||
sslcert=data['sslcert'] if is_ssl else None,
|
||||
sslkey=data['sslkey'] if is_ssl else None,
|
||||
sslrootcert=data['sslrootcert'] if is_ssl else None,
|
||||
sslcrl=data['sslcrl'] if is_ssl else None,
|
||||
sslcompression=1 if is_ssl and data['sslcompression'] else 0
|
||||
)
|
||||
db.session.add(server)
|
||||
db.session.commit()
|
||||
|
||||
connected = False
|
||||
icon = "icon-server-not-connected"
|
||||
user = None
|
||||
manager = None
|
||||
|
||||
@@ -574,14 +691,21 @@ class ServerNode(PGChildNodeView):
|
||||
if 'password' in data and data["password"] != '':
|
||||
# login with password
|
||||
have_password = True
|
||||
passfile = None
|
||||
password = data['password']
|
||||
password = encrypt(password, current_user.password)
|
||||
elif 'passfile' in data and data["passfile"] != '':
|
||||
passfile = data['passfile']
|
||||
setattr(server, 'passfile', passfile)
|
||||
db.session.commit()
|
||||
else:
|
||||
# Attempt password less login
|
||||
password = None
|
||||
passfile = None
|
||||
|
||||
status, errmsg = conn.connect(
|
||||
password=password,
|
||||
passfile=passfile,
|
||||
server_types=ServerType.types()
|
||||
)
|
||||
if hasattr(str, 'decode') and errmsg is not None:
|
||||
@@ -663,21 +787,16 @@ class ServerNode(PGChildNodeView):
|
||||
def dependents(self, gid, sid):
|
||||
return make_json_response(data='')
|
||||
|
||||
def module_js(self, **kwargs):
|
||||
def supported_servers(self, **kwargs):
|
||||
"""
|
||||
This property defines (if javascript) exists for this node.
|
||||
Override this property for your own logic.
|
||||
"""
|
||||
username = 'postgres'
|
||||
if config.SERVER_MODE is True:
|
||||
username = current_user.email.split('@')[0]
|
||||
|
||||
return make_response(
|
||||
render_template(
|
||||
"servers/servers.js",
|
||||
server_types=ServerType.types(),
|
||||
_=gettext,
|
||||
username=username,
|
||||
"servers/supported_servers.js",
|
||||
server_types=ServerType.types()
|
||||
),
|
||||
200, {'Content-Type': 'application/x-javascript'}
|
||||
)
|
||||
@@ -732,6 +851,7 @@ class ServerNode(PGChildNodeView):
|
||||
) if request.data else {}
|
||||
|
||||
password = None
|
||||
passfile = None
|
||||
save_password = False
|
||||
|
||||
# Connect the Server
|
||||
@@ -740,7 +860,8 @@ class ServerNode(PGChildNodeView):
|
||||
|
||||
if 'password' not in data:
|
||||
conn_passwd = getattr(conn, 'password', None)
|
||||
if conn_passwd is None and server.password is None:
|
||||
if conn_passwd is None and server.password is None and \
|
||||
server.passfile is None:
|
||||
# Return the password template in case password is not
|
||||
# provided, or password has not been saved earlier.
|
||||
return make_json_response(
|
||||
@@ -753,6 +874,10 @@ class ServerNode(PGChildNodeView):
|
||||
_=gettext
|
||||
)
|
||||
)
|
||||
elif server.passfile and server.passfile != '':
|
||||
passfile = server.passfile
|
||||
else:
|
||||
password = conn_passwd or server.password
|
||||
else:
|
||||
password = data['password'] if 'password' in data else None
|
||||
save_password = \
|
||||
@@ -771,6 +896,7 @@ class ServerNode(PGChildNodeView):
|
||||
try:
|
||||
status, errmsg = conn.connect(
|
||||
password=password,
|
||||
passfile=passfile,
|
||||
server_types=ServerType.types()
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -1052,17 +1178,21 @@ class ServerNode(PGChildNodeView):
|
||||
# Execute SQL to pause or resume WAL replay
|
||||
if conn.connected():
|
||||
if pause:
|
||||
status, res = conn.execute_scalar(
|
||||
"SELECT pg_xlog_replay_pause();"
|
||||
)
|
||||
sql = "SELECT pg_xlog_replay_pause();"
|
||||
if manager.version >= 100000:
|
||||
sql = "SELECT pg_wal_replay_pause();"
|
||||
|
||||
status, res = conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(
|
||||
errormsg=str(res)
|
||||
)
|
||||
else:
|
||||
status, res = conn.execute_scalar(
|
||||
"SELECT pg_xlog_replay_resume();"
|
||||
)
|
||||
sql = "SELECT pg_xlog_replay_resume();"
|
||||
if manager.version >= 100000:
|
||||
sql = "SELECT pg_wal_replay_resume();"
|
||||
|
||||
status, res = conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(
|
||||
errormsg=str(res)
|
||||
|
||||
@@ -14,7 +14,7 @@ import re
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers as servers
|
||||
from flask import render_template, make_response, current_app, request, jsonify
|
||||
from flask import render_template, current_app, request, jsonify
|
||||
from flask_babel import gettext as _
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.server_groups.servers.databases.utils import \
|
||||
@@ -78,6 +78,13 @@ class DatabaseModule(CollectionNodeModule):
|
||||
|
||||
return snippets
|
||||
|
||||
@property
|
||||
def module_use_template_javascript(self):
|
||||
"""
|
||||
Returns whether Jinja2 template is used for generating the javascript
|
||||
module.
|
||||
"""
|
||||
return False
|
||||
|
||||
blueprint = DatabaseModule(__name__)
|
||||
|
||||
@@ -106,7 +113,6 @@ class DatabaseView(PGChildNodeView):
|
||||
'dependency': [{'get': 'dependencies'}],
|
||||
'dependent': [{'get': 'dependents'}],
|
||||
'children': [{'get': 'children'}],
|
||||
'module.js': [{}, {}, {'get': 'module_js'}],
|
||||
'connect': [{
|
||||
'get': 'connect_status', 'post': 'connect', 'delete': 'disconnect'
|
||||
}],
|
||||
@@ -137,8 +143,8 @@ class DatabaseView(PGChildNodeView):
|
||||
self.db_allow_connection = True
|
||||
# If connection to database is not allowed then
|
||||
# provide generic connection
|
||||
if kwargs['did'] in self.conn.manager.db_info:
|
||||
self._db = self.conn.manager.db_info[kwargs['did']]
|
||||
if kwargs['did'] in self.manager.db_info:
|
||||
self._db = self.manager.db_info[kwargs['did']]
|
||||
if self._db['datallowconn'] is False:
|
||||
self.conn = self.manager.connection()
|
||||
self.db_allow_connection = False
|
||||
@@ -160,11 +166,18 @@ class DatabaseView(PGChildNodeView):
|
||||
(self.manager.db_info[self.manager.did])['datlastsysoid'] \
|
||||
if self.manager.db_info is not None and \
|
||||
self.manager.did in self.manager.db_info else 0
|
||||
|
||||
db_disp_res = None
|
||||
params = None
|
||||
if self.manager and self.manager.db_res:
|
||||
db_disp_res = ", ".join(['%s'] * len(self.manager.db_res.split(',')))
|
||||
params = tuple(self.manager.db_res.split(','))
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
conn=self.conn, last_system_oid=last_system_oid
|
||||
conn=self.conn, last_system_oid=last_system_oid, db_restrictions=db_disp_res
|
||||
)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
status, res = self.conn.execute_dict(SQL, params)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
@@ -182,12 +195,19 @@ class DatabaseView(PGChildNodeView):
|
||||
if self.manager.db_info is not None and \
|
||||
self.manager.did in self.manager.db_info else 0
|
||||
)
|
||||
server_node_res = self.manager
|
||||
|
||||
db_disp_res = None
|
||||
params = None
|
||||
if server_node_res and server_node_res.db_res:
|
||||
db_disp_res = ", ".join(['%s']*len(server_node_res.db_res.split(',')))
|
||||
params = tuple(server_node_res.db_res.split(','))
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'nodes.sql']),
|
||||
last_system_oid=last_system_oid
|
||||
last_system_oid=last_system_oid,
|
||||
db_restrictions=db_disp_res
|
||||
)
|
||||
status, rset = self.conn.execute_dict(SQL)
|
||||
status, rset = self.conn.execute_dict(SQL, params)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
@@ -275,27 +295,26 @@ class DatabaseView(PGChildNodeView):
|
||||
|
||||
@check_precondition(action="properties")
|
||||
def properties(self, gid, sid, did):
|
||||
conn = self.manager.connection()
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
did=did, conn=conn, last_system_oid=0
|
||||
did=did, conn=self.conn, last_system_oid=0
|
||||
)
|
||||
status, res = conn.execute_dict(SQL)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
_("Could not find the database on the server.")
|
||||
)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'acl.sql']),
|
||||
did=did, conn=conn
|
||||
did=did, conn=self.conn
|
||||
)
|
||||
status, dataclres = conn.execute_dict(SQL)
|
||||
status, dataclres = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
@@ -303,9 +322,9 @@ class DatabaseView(PGChildNodeView):
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'defacl.sql']),
|
||||
did=did, conn=conn
|
||||
did=did, conn=self.conn
|
||||
)
|
||||
status, defaclres = conn.execute_dict(SQL)
|
||||
status, defaclres = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
@@ -315,10 +334,10 @@ class DatabaseView(PGChildNodeView):
|
||||
# Fetching variable for database
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'get_variables.sql']),
|
||||
did=did, conn=conn
|
||||
did=did, conn=self.conn
|
||||
)
|
||||
|
||||
status, res1 = conn.execute_dict(SQL)
|
||||
status, res1 = self.conn.execute_dict(SQL)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res1)
|
||||
@@ -345,19 +364,6 @@ class DatabaseView(PGChildNodeView):
|
||||
res['rows'][0].setdefault(row['deftype'], []).append(priv)
|
||||
return res
|
||||
|
||||
def module_js(self):
|
||||
"""
|
||||
This property defines (if javascript) exists for this node.
|
||||
Override this property for your own logic.
|
||||
"""
|
||||
return make_response(
|
||||
render_template(
|
||||
"databases/js/databases.js",
|
||||
_=_
|
||||
),
|
||||
200, {'Content-Type': 'application/x-javascript'}
|
||||
)
|
||||
|
||||
def connect(self, gid, sid, did):
|
||||
"""Connect the Database."""
|
||||
from pgadmin.utils.driver import get_driver
|
||||
@@ -605,17 +611,44 @@ class DatabaseView(PGChildNodeView):
|
||||
# used for offline updates
|
||||
self.manager.release(conn_id="db_offline_update")
|
||||
|
||||
# Fetch the new data again after update for proper node
|
||||
# generation
|
||||
status, rset = self.conn.execute_dict(
|
||||
render_template(
|
||||
"/".join([self.template_path, 'nodes.sql']),
|
||||
did=did, conn=self.conn, last_system_oid=0
|
||||
)
|
||||
)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
if len(rset['rows']) == 0:
|
||||
return gone(
|
||||
_("Could not find the database on the server.")
|
||||
)
|
||||
|
||||
res = rset['rows'][0]
|
||||
|
||||
canDrop = canDisConn = True
|
||||
if self.manager.db == res['name']:
|
||||
canDrop = canDisConn = False
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
did,
|
||||
sid,
|
||||
data['name'],
|
||||
"pg-icon-{0}".format(self.node_type) if
|
||||
self._db['datallowconn'] else
|
||||
res['name'],
|
||||
icon="pg-icon-{0}".format(self.node_type) if
|
||||
self._db['datallowconn'] and self.conn.connected() else
|
||||
"icon-database-not-connected",
|
||||
connected=self.conn.connected() if
|
||||
self._db['datallowconn'] else False,
|
||||
allowConn=self._db['datallowconn']
|
||||
tablespace=res['spcname'],
|
||||
allowConn=res['datallowconn'],
|
||||
canCreate=res['cancreate'],
|
||||
canDisconn=canDisConn,
|
||||
canDrop=canDrop,
|
||||
inode=True if res['datallowconn'] else False
|
||||
)
|
||||
)
|
||||
|
||||
@@ -831,12 +864,17 @@ class DatabaseView(PGChildNodeView):
|
||||
if self.manager.db_info is not None and \
|
||||
self.manager.did in self.manager.db_info else 0
|
||||
|
||||
db_disp_res = None
|
||||
params = None
|
||||
if self.manager and self.manager.db_res:
|
||||
db_disp_res = ", ".join(['%s'] * len(self.manager.db_res.split(',')))
|
||||
params = tuple(self.manager.db_res.split(','))
|
||||
|
||||
conn = self.manager.connection()
|
||||
status, res = conn.execute_dict(
|
||||
render_template(
|
||||
"/".join([self.template_path, 'stats.sql']),
|
||||
did=did, conn=conn, last_system_oid=last_system_oid
|
||||
)
|
||||
status, res = conn.execute_dict(render_template(
|
||||
"/".join([self.template_path, 'stats.sql']),
|
||||
did=did, conn=conn, last_system_oid=last_system_oid, db_restrictions=db_disp_res
|
||||
),params
|
||||
)
|
||||
|
||||
if not status:
|
||||
@@ -859,9 +897,15 @@ class DatabaseView(PGChildNodeView):
|
||||
did=did, conn=conn, last_system_oid=0
|
||||
)
|
||||
status, res = conn.execute_dict(SQL)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
_("Could not find the database on the server.")
|
||||
)
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'acl.sql']),
|
||||
did=did, conn=self.conn
|
||||
|
||||
@@ -13,16 +13,18 @@ import simplejson as json
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers.databases as databases
|
||||
from flask import render_template, make_response, request, jsonify
|
||||
from flask import render_template, request, jsonify
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.utils import PGChildNodeView
|
||||
from pgadmin.utils.ajax import make_json_response, internal_server_error, \
|
||||
make_response as ajax_response, gone
|
||||
from pgadmin.utils.driver import get_driver
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
|
||||
from pgadmin.utils import IS_PY2
|
||||
# If we are in Python3
|
||||
if not IS_PY2:
|
||||
unicode = str
|
||||
|
||||
class CastModule(CollectionNodeModule):
|
||||
"""
|
||||
@@ -51,6 +53,7 @@ class CastModule(CollectionNodeModule):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CastModule, self).__init__(*args, **kwargs)
|
||||
self.min_gpdbver = 1000000000
|
||||
|
||||
def get_nodes(self, gid, sid, did):
|
||||
"""
|
||||
@@ -76,6 +79,14 @@ class CastModule(CollectionNodeModule):
|
||||
"""
|
||||
return databases.DatabaseModule.NODE_TYPE
|
||||
|
||||
@property
|
||||
def module_use_template_javascript(self):
|
||||
"""
|
||||
Returns whether Jinja2 template is used for generating the javascript
|
||||
module.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
blueprint = CastModule(__name__)
|
||||
|
||||
@@ -93,10 +104,6 @@ class CastView(PGChildNodeView):
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the CastView and it's base view.
|
||||
|
||||
* module_js()
|
||||
- This property defines (if javascript) exists for this node.
|
||||
Override this property for your own logic
|
||||
|
||||
* check_precondition()
|
||||
- This function will behave as a decorator which will checks
|
||||
database connection before running view, it will also attaches
|
||||
@@ -164,7 +171,6 @@ class CastView(PGChildNodeView):
|
||||
'stats': [{'get': 'statistics'}],
|
||||
'dependency': [{'get': 'dependencies'}],
|
||||
'dependent': [{'get': 'dependents'}],
|
||||
'module.js': [{}, {}, {'get': 'module_js'}],
|
||||
'get_type': [{'get': 'get_src_and_trg_type'}, {'get': 'get_src_and_trg_type'}],
|
||||
'get_functions': [{'post': 'get_functions'}, {'post': 'get_functions'}]
|
||||
})
|
||||
@@ -175,18 +181,6 @@ class CastView(PGChildNodeView):
|
||||
self.manager = None
|
||||
super(CastView, self).__init__(**kwargs)
|
||||
|
||||
def module_js(self):
|
||||
"""
|
||||
This property defines whether javascript exists for this node.
|
||||
"""
|
||||
return make_response(
|
||||
render_template(
|
||||
"cast/js/casts.js",
|
||||
_=gettext
|
||||
),
|
||||
200, {'Content-Type': 'application/x-javascript'}
|
||||
)
|
||||
|
||||
def check_precondition(f):
|
||||
"""
|
||||
This function will behave as a decorator which will check the
|
||||
@@ -413,6 +407,9 @@ class CastView(PGChildNodeView):
|
||||
)
|
||||
try:
|
||||
sql, name = self.get_sql(gid, sid, did, data, cid)
|
||||
# Most probably this is due to error
|
||||
if not isinstance(sql, (str, unicode)):
|
||||
return sql
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
@@ -503,6 +500,9 @@ class CastView(PGChildNodeView):
|
||||
"""
|
||||
data = request.args
|
||||
sql, name = self.get_sql(gid, sid, did, data, cid)
|
||||
# Most probably this is due to error
|
||||
if not isinstance(sql, (str, unicode)):
|
||||
return sql
|
||||
sql = sql.strip('\n').strip(' ')
|
||||
if sql == '':
|
||||
sql = "--modified SQL"
|
||||
@@ -536,6 +536,11 @@ class CastView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
_("Could not find the specified cast on the server.")
|
||||
)
|
||||
|
||||
old_data = res['rows'][0]
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'update.sql']),
|
||||
|
||||
|
Before Width: | Height: | Size: 426 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#def4fd;}.cls-2{fill:#354a5f;}.cls-3{fill:#2880b9;}.cls-4,.cls-6{fill:#2980b9;}.cls-4,.cls-5{stroke:#2980b9;}.cls-4,.cls-5,.cls-6,.cls-7{stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}.cls-5,.cls-7{fill:none;}.cls-6,.cls-7{stroke:#354a5f;}</style></defs><title>cast</title><g id="_3" data-name="3"><rect class="cls-1" x="8.99" y="2.39" width="4.65" height="4.65" rx="0.63" ry="0.63"/><path class="cls-2" d="M13,2.77a.25.25,0,0,1,.25.25v3.4a.25.25,0,0,1-.25.25H9.62a.25.25,0,0,1-.25-.25V3a.25.25,0,0,1,.25-.25H13M13,2H9.62a1,1,0,0,0-1,1v3.4a1,1,0,0,0,1,1H13a1,1,0,0,0,1-1V3a1,1,0,0,0-1-1Z"/><rect class="cls-1" x="2.36" y="8.96" width="4.65" height="4.65" rx="0.63" ry="0.63"/><path class="cls-3" d="M6.38,9.33a.25.25,0,0,1,.25.25V13a.25.25,0,0,1-.25.25H3A.25.25,0,0,1,2.73,13V9.58A.25.25,0,0,1,3,9.33h3.4m0-.75H3a1,1,0,0,0-1,1V13a1,1,0,0,0,1,1h3.4a1,1,0,0,0,1-1V9.58a1,1,0,0,0-1-1Z"/><polygon class="cls-4" points="4.67 7.83 3.69 6.82 5.68 6.85 4.67 7.83"/><path class="cls-5" d="M4.77,7.33c-.65-2.21,1-3.5,2.92-2.7"/><polygon class="cls-6" points="11.25 8.23 12.23 9.25 10.23 9.21 11.25 8.23"/><path class="cls-7" d="M11.14,8.74c.65,2.21-1,3.5-2.92,2.7"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 402 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#def4fd;}.cls-2{fill:#717f8e;}.cls-3{fill:#354a5f;}.cls-4{fill:#2880b9;}.cls-5,.cls-7{fill:#2980b9;}.cls-5,.cls-6{stroke:#2980b9;}.cls-5,.cls-6,.cls-7,.cls-8{stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}.cls-6,.cls-8{fill:none;}.cls-7,.cls-8{stroke:#354a5f;}</style></defs><title>coll-cast</title><g id="_3" data-name="3"><rect class="cls-1" x="2.26" y="2.25" width="10.53" height="10.53" rx="0.75" ry="0.75"/><path class="cls-2" d="M12,2.5a.5.5,0,0,1,.5.5v9a.5.5,0,0,1-.5.5H3a.5.5,0,0,1-.5-.5V3A.5.5,0,0,1,3,2.5h9M12,2H3A1,1,0,0,0,2,3v9a1,1,0,0,0,1,1h9a1,1,0,0,0,1-1V3a1,1,0,0,0-1-1Z"/><rect class="cls-1" x="3.21" y="3.21" width="10.53" height="10.53" rx="0.75" ry="0.75"/><path class="cls-3" d="M13,3.46a.5.5,0,0,1,.5.5v9a.5.5,0,0,1-.5.5H4a.5.5,0,0,1-.5-.5V4a.5.5,0,0,1,.5-.5h9M13,3H4A1,1,0,0,0,3,4v9a1,1,0,0,0,1,1h9a1,1,0,0,0,1-1V4a1,1,0,0,0-1-1Z"/><rect class="cls-1" x="9.28" y="4.67" width="3.03" height="3.03" rx="0.63" ry="0.63"/><path class="cls-3" d="M11.69,5a.25.25,0,0,1,.25.25V7.07a.25.25,0,0,1-.25.25H9.91a.25.25,0,0,1-.25-.25V5.29A.25.25,0,0,1,9.91,5h1.78m0-.75H9.91a1,1,0,0,0-1,1V7.07a1,1,0,0,0,1,1h1.78a1,1,0,0,0,1-1V5.29a1,1,0,0,0-1-1Z"/><rect class="cls-1" x="4.64" y="9.26" width="3.03" height="3.03" rx="0.63" ry="0.63"/><path class="cls-4" d="M7,9.64a.25.25,0,0,1,.25.25v1.78a.25.25,0,0,1-.25.25H5.26A.25.25,0,0,1,5,11.67V9.89a.25.25,0,0,1,.25-.25H7m0-.75H5.26a1,1,0,0,0-1,1v1.78a1,1,0,0,0,1,1H7a1,1,0,0,0,1-1V9.89a1,1,0,0,0-1-1Z"/><polygon class="cls-5" points="6.14 8.36 5.46 7.66 6.85 7.68 6.14 8.36"/><path class="cls-6" d="M6.21,8c-.45-1.55.71-2.45,2-1.89"/><polygon class="cls-7" points="10.75 8.65 11.43 9.36 10.04 9.33 10.75 8.65"/><path class="cls-8" d="M10.67,9c.45,1.55-.71,2.45-2,1.89"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,13 +1,13 @@
|
||||
define(
|
||||
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
|
||||
function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
define('pgadmin.node.cast', [
|
||||
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin',
|
||||
'pgadmin.browser', 'pgadmin.alertifyjs', 'pgadmin.browser.collection'
|
||||
], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
// Extend the collection class for cast
|
||||
if (!pgBrowser.Nodes['coll-cast']) {
|
||||
var casts = pgAdmin.Browser.Nodes['coll-cast'] =
|
||||
pgAdmin.Browser.Collection.extend({
|
||||
node: 'cast',
|
||||
label: '{{ _('Casts') }}',
|
||||
label: gettext('Casts'),
|
||||
type: 'coll-cast',
|
||||
columns: ['name', 'description']
|
||||
});
|
||||
@@ -20,10 +20,10 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
type: 'cast',
|
||||
sqlAlterHelp: 'sql-altercast.html',
|
||||
sqlCreateHelp: 'sql-createcast.html',
|
||||
dialogHelp: '{{ url_for('help.static', filename='cast_dialog.html') }}',
|
||||
dialogHelp: url_for('help.static', {'filename': 'cast_dialog.html'}),
|
||||
canDrop: true,
|
||||
canDropCascade: true,
|
||||
label: '{{ _('Cast') }}',
|
||||
label: gettext('Cast'),
|
||||
hasSQL: true,
|
||||
hasDepends: true,
|
||||
Init: function() {
|
||||
@@ -38,18 +38,18 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_cast_on_database', node: 'database', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Cast...') }}',
|
||||
category: 'create', priority: 4, label: gettext('Cast...'),
|
||||
icon: 'wcTabIcon icon-cast', data: {action: 'create'},
|
||||
enable: pgBrowser.Nodes['database'].is_conn_allow
|
||||
},{
|
||||
name: 'create_cast_on_coll', node: 'coll-cast', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Cast...') }}',
|
||||
category: 'create', priority: 4, label: gettext('Cast...'),
|
||||
icon: 'wcTabIcon icon-cast', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_cast', node: 'cast', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Cast...') }}',
|
||||
category: 'create', priority: 4, label: gettext('Cast...'),
|
||||
icon: 'wcTabIcon icon-cast', data: {action: 'create'}
|
||||
}]);
|
||||
|
||||
@@ -70,14 +70,14 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
// Define the schema for cast
|
||||
schema: [{
|
||||
id: 'name', label: '{{ _('Name') }}', cell: 'string',
|
||||
id: 'name', label: gettext('Name'), cell: 'string',
|
||||
editable: false, type: 'text', disabled: true, cellHeaderClasses: 'width_percent_50'
|
||||
},{
|
||||
id: 'oid', label:'{{ _('OID') }}', cell: 'string',
|
||||
id: 'oid', label: gettext('OID'), cell: 'string',
|
||||
editable: false, type: 'text', disabled: true, mode: ['properties'],
|
||||
},{
|
||||
id: 'srctyp', label:'{{ _('Source type') }}', url: 'get_type',
|
||||
type: 'text', group: 'Definition', disabled: function(m) {
|
||||
id: 'srctyp', label: gettext('Source type'), url: 'get_type',
|
||||
type: 'text', group: gettext('Definition'), disabled: function(m) {
|
||||
return !m.isNew()
|
||||
}, mode: ['create'],
|
||||
|
||||
@@ -120,11 +120,11 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
* edit mode only
|
||||
*/
|
||||
{
|
||||
id: 'srctyp', label:'{{ _('Source type') }}', type: 'text',
|
||||
group: 'Definition', disabled: true, mode:['properties','edit']
|
||||
id: 'srctyp', label: gettext('Source type'), type: 'text',
|
||||
group: gettext('Definition'), disabled: true, mode:['properties','edit']
|
||||
},{
|
||||
id: 'trgtyp', label:'{{ _('Target type') }}', url: 'get_type',
|
||||
type: 'text', group: 'Definition', disabled: function(m) {
|
||||
id: 'trgtyp', label: gettext('Target type'), url: 'get_type',
|
||||
type: 'text', group: gettext('Definition'), disabled: function(m) {
|
||||
return !m.isNew()
|
||||
}, mode: ['create'],
|
||||
transform: function(rows) {
|
||||
@@ -165,8 +165,8 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
* edit mode only
|
||||
*/
|
||||
{
|
||||
id: 'trgtyp', label:'{{ _('Target type') }}', type: 'text',
|
||||
group: 'Definition', disabled: true, mode:['properties','edit']
|
||||
id: 'trgtyp', label: gettext('Target type'), type: 'text',
|
||||
group: gettext('Definition'), disabled: true, mode:['properties','edit']
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -175,9 +175,9 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
* associated functions will be fetch using ajax call
|
||||
*/
|
||||
{
|
||||
id: 'proname', label:'{{ _('Function') }}', deps:['srctyp', 'trgtyp'],
|
||||
id: 'proname', label: gettext('Function'), deps:['srctyp', 'trgtyp'],
|
||||
type: 'text', disabled: function(m) { return !m.isNew(); },
|
||||
group: 'Definition', mode: ['create'],
|
||||
group: gettext('Definition'), mode: ['create'],
|
||||
control: 'node-ajax-options',
|
||||
options: function(control) {
|
||||
var srcTyp = control.model.get('srctyp');
|
||||
@@ -226,12 +226,12 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
* edit mode only
|
||||
*/
|
||||
{
|
||||
id: 'proname', label:'{{ _('Function') }}', type: 'text',
|
||||
group: 'Definition', disabled: true, mode:['properties','edit']
|
||||
id: 'proname', label: gettext('Function'), type: 'text',
|
||||
group: gettext('Definition'), disabled: true, mode:['properties','edit']
|
||||
},{
|
||||
id: 'castcontext', label:'{{ _('Context') }}',
|
||||
id: 'castcontext', label: gettext('Context'),
|
||||
options:{'onText':'IMPLICIT','offText':'EXPLICIT'},
|
||||
editable: false, type: 'string', group: 'Definition',
|
||||
editable: false, type: 'string', group: gettext('Definition'),
|
||||
mode:['create'],
|
||||
control: Backform.SwitchControl.extend({
|
||||
getValueFromDOM: function() {
|
||||
@@ -244,17 +244,17 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
* edit mode
|
||||
*/
|
||||
{
|
||||
id: 'castcontext', label:'{{ _('Context') }}', disabled: true,
|
||||
id: 'castcontext', label: gettext('Context'), disabled: true,
|
||||
options:[{
|
||||
label: 'IMPLICIT', value: 'IMPLICIT'
|
||||
},{
|
||||
label: 'EXPLICIT', value: 'EXPLICIT'
|
||||
},{
|
||||
label: 'ASSIGNMENT', value: 'ASSIGNMENT'
|
||||
}], editable: false, type: 'select2', group: 'Definition',
|
||||
}], editable: false, type: 'select2', group: gettext('Definition'),
|
||||
mode:['properties', 'edit']
|
||||
},{
|
||||
id: 'syscast', label:'{{ _('System cast?') }}',
|
||||
id: 'syscast', label: gettext('System cast?'),
|
||||
cell: 'switch', type: 'switch', mode: ['properties'], disabled: true,
|
||||
options: {
|
||||
'onText': 'Yes', 'offText': 'No',
|
||||
@@ -262,7 +262,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
'size': 'small'
|
||||
}
|
||||
},{
|
||||
id: 'description', label:'{{ _('Comment') }}',type: 'text',
|
||||
id: 'description', label: gettext('Comment'),type: 'text',
|
||||
type: 'multiline', cellHeaderClasses: 'width_percent_50'
|
||||
}
|
||||
],
|
||||
@@ -279,7 +279,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
// validate source type control
|
||||
if (_.isUndefined(srctype) || _.isNull(srctype) || String(srctype).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Source type must be selected.') }}';
|
||||
var msg = gettext('Source type must be selected.');
|
||||
this.errorModel.set('srctyp', msg);
|
||||
return msg;
|
||||
}
|
||||
@@ -290,7 +290,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
// validate target type control
|
||||
if (_.isUndefined(trgtype) || _.isNull(trgtype) || String(trgtype).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Target type must be selected.') }}';
|
||||
var msg = gettext('Target type must be selected.');
|
||||
this.errorModel.set('trgtyp', msg);
|
||||
return msg;
|
||||
}
|
||||
@@ -52,7 +52,8 @@ class CastsAddTestCase(BaseTestGenerator):
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'])
|
||||
self.server['port'],
|
||||
self.server['sslmode'])
|
||||
cast_utils.drop_cast(connection, self.data["srctyp"],
|
||||
self.data["trgtyp"])
|
||||
database_utils.disconnect_database(self, self.server_id,
|
||||
|
||||
@@ -48,7 +48,8 @@ class CastsDeleteTestCase(BaseTestGenerator):
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'])
|
||||
self.server['port'],
|
||||
self.server['sslmode'])
|
||||
response = cast_utils.verify_cast(connection, self.source_type,
|
||||
self.target_type)
|
||||
if len(response) == 0:
|
||||
|
||||
@@ -58,7 +58,8 @@ class CastsGetTestCase(BaseTestGenerator):
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'])
|
||||
self.server['port'],
|
||||
self.server['sslmode'])
|
||||
cast_utils.drop_cast(connection, self.source_type,
|
||||
self.target_type)
|
||||
database_utils.disconnect_database(self, self.server_id,
|
||||
|
||||
@@ -43,7 +43,8 @@ def create_cast(server, source_type, target_type):
|
||||
server['username'],
|
||||
server['db_password'],
|
||||
server['host'],
|
||||
server['port'])
|
||||
server['port'],
|
||||
server['sslmode'])
|
||||
old_isolation_level = connection.isolation_level
|
||||
connection.set_isolation_level(0)
|
||||
pg_cursor = connection.cursor()
|
||||
|
||||
@@ -12,16 +12,18 @@ import re
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers.databases as database
|
||||
from flask import render_template, make_response, request, jsonify
|
||||
from flask import render_template, request, jsonify
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.utils import PGChildNodeView
|
||||
from pgadmin.utils.ajax import make_json_response, internal_server_error, \
|
||||
make_response as ajax_response
|
||||
make_response as ajax_response, gone
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from pgadmin.utils.ajax import gone
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.utils import IS_PY2
|
||||
# If we are in Python3
|
||||
if not IS_PY2:
|
||||
unicode = str
|
||||
|
||||
|
||||
class EventTriggerModule(CollectionNodeModule):
|
||||
@@ -58,6 +60,7 @@ class EventTriggerModule(CollectionNodeModule):
|
||||
super(EventTriggerModule, self).__init__(*args, **kwargs)
|
||||
self.min_ver = 90300
|
||||
self.max_ver = None
|
||||
self.min_gpdbver = 1000000000
|
||||
|
||||
def get_nodes(self, gid, sid, did):
|
||||
"""
|
||||
@@ -81,6 +84,14 @@ class EventTriggerModule(CollectionNodeModule):
|
||||
"""
|
||||
return database.DatabaseModule.NODE_TYPE
|
||||
|
||||
@property
|
||||
def module_use_template_javascript(self):
|
||||
"""
|
||||
Returns whether Jinja2 template is used for generating the javascript
|
||||
module.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
blueprint = EventTriggerModule(__name__)
|
||||
|
||||
@@ -99,9 +110,6 @@ class EventTriggerView(PGChildNodeView):
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the EventTriggerView and it's base view.
|
||||
|
||||
* module_js()
|
||||
- Returns the javascript module for event trigger.
|
||||
|
||||
* check_precondition()
|
||||
- This function will behave as a decorator which will checks
|
||||
database connection before running view, it will also attaches
|
||||
@@ -163,22 +171,9 @@ class EventTriggerView(PGChildNodeView):
|
||||
'stats': [{'get': 'statistics'}],
|
||||
'dependency': [{'get': 'dependencies'}],
|
||||
'dependent': [{'get': 'dependents'}],
|
||||
'module.js': [{}, {}, {'get': 'module_js'}],
|
||||
'fopts': [{'get': 'get_event_funcs'}, {'get': 'get_event_funcs'}]
|
||||
})
|
||||
|
||||
def module_js(self):
|
||||
"""
|
||||
Returns the javascript module for event trigger.
|
||||
"""
|
||||
return make_response(
|
||||
render_template(
|
||||
"event_triggers/js/event_trigger.js",
|
||||
_=gettext
|
||||
),
|
||||
200, {'Content-Type': 'application/x-javascript'}
|
||||
)
|
||||
|
||||
def check_precondition(f):
|
||||
"""
|
||||
This function will behave as a decorator which will checks
|
||||
@@ -427,6 +422,9 @@ class EventTriggerView(PGChildNodeView):
|
||||
|
||||
try:
|
||||
sql = self.get_sql(data, etid)
|
||||
# Most probably this is due to error
|
||||
if not isinstance(sql, (str, unicode)):
|
||||
return sql
|
||||
sql = sql.strip('\n').strip(' ')
|
||||
if sql != "":
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
@@ -539,7 +537,11 @@ class EventTriggerView(PGChildNodeView):
|
||||
data[k] = v
|
||||
try:
|
||||
sql = self.get_sql(data, etid)
|
||||
# Most probably this is due to error
|
||||
if not isinstance(sql, (str, unicode)):
|
||||
return sql
|
||||
sql = sql.strip('\n').strip(' ')
|
||||
sql = re.sub('\n{2,}', '\n\n', sql)
|
||||
if sql == '':
|
||||
sql = "--modified SQL"
|
||||
return make_json_response(
|
||||
@@ -628,6 +630,11 @@ class EventTriggerView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
_("Could not find the specified event trigger on the server.")
|
||||
)
|
||||
|
||||
result = res['rows'][0]
|
||||
result = self._formatter(result)
|
||||
|
||||
@@ -648,6 +655,7 @@ class EventTriggerView(PGChildNodeView):
|
||||
sql_header += "\n"
|
||||
|
||||
sql = sql_header + sql
|
||||
sql = re.sub('\n{2,}', '\n\n', sql)
|
||||
|
||||
return ajax_response(response=sql)
|
||||
|
||||
|
||||