Compare commits

...
33 Commits
Author SHA1 Message Date
Rahul Shirsat ab04b30726 1. Unverified email id is getting locked.
2. Admin should be able to lock the user, as currently it only unlocks it via
   user management dialog.
3. There were some indefinite login page loading issues when trying to log in
   with invalid password, where it should redirect to the login page again instead.

refs #6337 (Initial patch by Khushboo Vashi)
2021-08-09 21:04:49 +05:30
Yogesh Mahajan dc4f59e664 Handle null values more correctly.
refs #5198
2021-08-09 20:11:39 +05:30
Akshay Joshi 8738f6dd98 Update version for release. 2021-08-09 14:44:52 +05:30
Akshay Joshi 039766b492 Update DEPENDENCIES. 2021-08-09 14:42:24 +05:30
Libor M 2da970a26c Update Czech translation. 2021-08-09 14:34:32 +05:30
fn ln 86e1f33231 Update Japanese translation. 2021-08-09 14:33:02 +05:30
Domenico Sgarbossa 702fbac1c0 Update Italian translation. 2021-08-09 14:31:37 +05:30
Pradip Parkale 7483ddf2b2 Added support to copy SQL from main window to query tool. Fixes #4904 2021-08-09 14:29:31 +05:30
Khushboo Vashi 78b041465e Fixed an issue where even if the user is locked, he can reset the password and can login into pgAdmin. Fixes #6664 2021-08-09 14:24:26 +05:30
Akshay Joshi 5e3aa305fd Update message catalogs. 2021-08-06 19:35:30 +05:30
Nikhil Mohite 703478c13d Fixed an issue where the titles in query tabs are different. Fixes #6618 2021-08-06 19:09:39 +05:30
Yogesh Mahajan 8d53c10643 Fixed styling for JSON Editor to match with pgAdmin4 style.
refs #5198
2021-08-06 18:01:50 +05:30
Akshay Joshi 025c98ced8 1) Flash warning message of N no of attempts left for the unsuccessful login attempt.
2) Updated some strings in the Preferences for the "Maximum column width" option.
2021-08-03 17:06:06 +05:30
Yogesh Mahajan 8e214ca8fb Added support for formatted JSON viewer/editor when interacting with data in a JSON column. Fixes #5198 2021-08-03 11:36:45 +05:30
Akshay Joshi 23265061b2 Fixed an issue where an empty SQLite database file is created when try to get the version. 2021-07-30 15:44:46 +05:30
Aditya Toshniwal 9d985d3d8f Fixed potential bug in the table node. 2021-07-30 13:28:07 +05:30
Akshay Joshi 03477d407e Fixed dashboard server activity issue when active_since parameter is None. Fixes #6650 2021-07-29 11:49:27 +05:30
Rahul Shirsat 2dd88e5a4e Ensure that all the required options should be loaded when the Range data type
is selected while creating a custom data type.

Fixes #6643
2021-07-29 10:43:27 +05:30
Aditya Toshniwal 2452d90300 Fixed incorrect tablespace options in the drop-down for move objects dialog. Fixes #6586 2021-07-27 17:28:38 +05:30
Akshay Joshi 508f97b08e Rename the "Resize by data?" to "Columns sized by" and disabled the 'Maximum column width'
button if 'Columns sized by' is set to 'Column data'. Fixes #6622
2021-07-27 15:17:06 +05:30
Akshay Joshi 3f67f512ec Fixed incorrect binary path issue when the user deletes the binary path from the preferences. Fixes #6619 2021-07-26 11:49:48 +05:30
Akshay Joshi fdc3028124 Fixed linter error. 2021-07-23 12:57:40 +05:30
zhangjie2@fujitsu.com 3a3eea0320 Correct the string for shared server test case. 2021-07-23 12:44:16 +05:30
Nikhil Mohite d95a6f2af7 Fixed an issue where paste is not working through Right-Click option on PSQL. Fixes #6574 2021-07-23 12:41:48 +05:30
Florian Sabonchi a3d3c74e67 Ensure that the login account should be locked after N number of attempts. N is configurable using the 'MAX_LOGIN_ATTEMPTS' parameter. Fixes #6337 2021-07-22 12:24:43 +05:30
Dave Page c2db647379 Run apt update before doing anything else. 2021-07-21 15:48:59 +01:00
Rahul Shirsat 6cc3423cbe Fixed session expiration API test case. 2021-07-20 16:29:03 +05:30
Akshay Joshi eb658a75ab Modified the bulk insert logic to the user table in the migration file. 2021-07-20 13:27:03 +05:30
Pradip Parkale b2205fc6e1 Fixed an issue in the search object when searching in 'all types' or 'subscription' if the user doesn't have access to the subscription. Fixes #6448 2021-07-19 14:11:10 +05:30
Nikhil Mohite 5768ade198 Added exception handling for SQLAlchemy function to check the table exists or not. 2021-07-19 14:01:45 +05:30
Rahul Shirsat b0727cc532 Fixed CSRF errors for stale sessions by increasing the session expiration time for desktop mode. Fixes #6369 2021-07-15 17:49:42 +05:30
Rahul Shirsat 9e8af67202 Fixed TypeError 'NoneType' object is not sub scriptable. Fixes #6580 2021-07-15 17:46:31 +05:30
Fred Escallier 69d0fbe419 Update French translation. 2021-07-15 17:38:57 +05:30
79 changed files with 5397 additions and 3707 deletions

No files matched your search

+22 -12
View File
@@ -227,6 +227,7 @@ is-arrayish 0.2.1
is-color-stop 1.1.0 MIT git+https://github.com/pigcan/is-color-stop.git
is-finite 1.1.0 MIT https://github.com/sindresorhus/is-finite.git
istanbul-instrumenter-loader 3.0.1 MIT https://github.com/webpack-contrib/istanbul-instrumenter-loader.git
javascript-natural-sort 0.7.1 MIT https://github.com/Bill4Time/javascript-natural-sort.git
jquery 3.6.0 MIT https://github.com/jquery/jquery.git
jquery-contextmenu 2.9.2 MIT git://github.com/swisnl/jQuery-contextMenu.git
jquery-ui 1.12.1 MIT git://github.com/jquery/jquery-ui.git
@@ -239,6 +240,7 @@ json-bignumber 1.0.2
json-parse-even-better-errors 2.3.1 MIT https://github.com/npm/json-parse-even-better-errors
json-schema-traverse 0.4.1 MIT git+https://github.com/epoberezkin/json-schema-traverse.git
json-schema-traverse 0.3.1 MIT git+https://github.com/epoberezkin/json-schema-traverse.git
json-source-map 0.6.1 MIT git+https://github.com/epoberezkin/json-source-map.git
json5 2.2.0 MIT git+https://github.com/json5/json5.git
json5 1.0.1 MIT git+https://github.com/json5/json5.git
karma-coverage 2.0.3 MIT git://github.com/karma-runner/karma-coverage.git
@@ -257,6 +259,7 @@ ml-array-max 1.2.3
ml-array-min 1.2.2 MIT git+https://github.com/mljs/array.git
ml-array-rescale 1.3.5 MIT git+https://github.com/mljs/array.git
ml-matrix 6.8.0 MIT https://github.com/mljs/matrix.git
mobius1-selectr 2.4.13 MIT git+https://github.com/Mobius1/Selectr.git
moment 2.29.1 MIT https://github.com/moment/moment.git
moment 2.24.0 MIT https://github.com/moment/moment.git
moment-timezone 0.5.33 MIT https://github.com/moment/moment-timezone.git
@@ -274,6 +277,7 @@ parseuri 0.0.6
path-type 4.0.0 MIT https://github.com/sindresorhus/path-type.git
pathfinding 0.4.18 MIT git://github.com/qiao/PathFinding.js.git
performance-now 2.1.0 MIT git://github.com/braveg1rl/performance-now.git
picomodal 3.0.0 MIT https://github.com/Nycto/PicoModal.git
popper.js 1.16.1 MIT git+https://github.com/FezVrasta/popper.js.git
postcss 8.3.0 MIT https://github.com/postcss/postcss.git
postcss-calc 8.0.0 MIT https://github.com/postcss/postcss-calc.git
@@ -355,12 +359,14 @@ xterm-addon-search 0.8.0
xterm-addon-web-links 0.4.0 MIT https://github.com/xtermjs/xterm.js
yeast 0.1.2 MIT https://github.com/unshiftio/yeast.git
@fortawesome/fontawesome-free 5.15.3 (CC-BY-4.0 AND OFL-1.1 AND MIT) https://github.com/FortAwesome/Font-Awesome
@sphinxxxx/color-conversion 2.2.2 ISC git+https://github.com/Sphinxxxx/color-conversion.git
@trysound/sax 0.1.1 ISC git://github.com/svg/sax.git
boolbase 1.0.0 ISC https://github.com/fb55/boolbase
css-declaration-sorter 6.0.3 ISC https://github.com/Siilwyn/css-declaration-sorter.git
electron-to-chromium 1.3.740 ISC https://github.com/kilian/electron-to-chromium/
icss-utils 5.1.0 ISC git+https://github.com/css-modules/icss-utils.git
is-resolvable 1.1.0 ISC https://github.com/shinnn/is-resolvable.git
jsonrepair 2.2.1 ISC https://github.com/josdejong/jsonrepair.git
lru-cache 6.0.0 ISC git://github.com/isaacs/node-lru-cache.git
minimatch 3.0.4 ISC git://github.com/isaacs/minimatch.git
postcss-modules-extract-imports 3.0.0 ISC https://github.com/css-modules/postcss-modules-extract-imports.git
@@ -369,10 +375,25 @@ postcss-modules-values 4.0.0
semver 7.3.5 ISC https://github.com/npm/node-semver
semver 5.7.1 ISC https://github.com/npm/node-semver
semver 6.3.0 ISC https://github.com/npm/node-semver
vanilla-picker 2.11.2 ISC https://github.com/Sphinxxxx/vanilla-picker.git
yallist 4.0.0 ISC git+https://github.com/isaacs/yallist.git
yaml 1.10.2 ISC https://github.com/eemeli/yaml.git
ace-builds 1.4.12 BSD-3-Clause https://github.com/ajaxorg/ace-builds.git
istanbul-lib-coverage 1.2.1 BSD-3-Clause git@github.com:istanbuljs/istanbuljs.git
istanbul-lib-coverage 3.0.0 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
istanbul-lib-instrument 1.10.2 BSD-3-Clause git@github.com:istanbuljs/istanbuljs.git
istanbul-lib-instrument 4.0.3 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
istanbul-lib-report 3.0.0 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
istanbul-lib-source-maps 4.0.0 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
istanbul-reports 3.0.2 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
source-map 0.5.7 BSD-3-Clause http://github.com/mozilla/source-map.git
source-map 0.6.1 BSD-3-Clause http://github.com/mozilla/source-map.git
source-map-js 0.6.2 BSD-3-Clause http://github.com/7rulnik/source-map.git
sprintf-js 1.1.2 BSD-3-Clause https://github.com/alexei/sprintf.js.git
bootstrap-datepicker 1.9.0 Apache-2.0 https://github.com/uxsolutions/bootstrap-datepicker.git
eve 0.5.4 Apache-2.0 git@github.com:adobe-webplatform/eve.git
jmespath 0.15.0 Apache-2.0 git://github.com/jmespath/jmespath.js
jsoneditor 9.5.3 Apache-2.0 https://github.com/josdejong/jsoneditor.git
paths-js 0.4.11 Apache-2.0 https://github.com/andreaferretti/paths-js.git
snapsvg 0.5.1 Apache-2.0 git@github.com:adobe-webplatform/Snap.svg.git
caniuse-lite 1.0.30001230 CC-BY-4.0 https://github.com/browserslist/caniuse-lite.git
@@ -388,22 +409,11 @@ nth-check 2.0.0
uri-js 4.4.1 BSD-2-Clause http://github.com/garycourt/uri-js
FileSaver 0.10.0 UNKNOWN Unknown
heap 0.2.5 UNKNOWN git://github.com/qiao/heap.js.git
istanbul-lib-coverage 1.2.1 BSD-3-Clause git@github.com:istanbuljs/istanbuljs.git
istanbul-lib-coverage 3.0.0 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
istanbul-lib-instrument 1.10.2 BSD-3-Clause git@github.com:istanbuljs/istanbuljs.git
istanbul-lib-instrument 4.0.3 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
istanbul-lib-report 3.0.0 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
istanbul-lib-source-maps 4.0.0 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
istanbul-reports 3.0.2 BSD-3-Clause git+ssh://git@github.com/istanbuljs/istanbuljs.git
source-map 0.5.7 BSD-3-Clause http://github.com/mozilla/source-map.git
source-map 0.6.1 BSD-3-Clause http://github.com/mozilla/source-map.git
source-map-js 0.6.2 BSD-3-Clause http://github.com/7rulnik/source-map.git
sprintf-js 1.1.2 BSD-3-Clause https://github.com/alexei/sprintf.js.git
mdn-data 2.0.14 CC0-1.0 https://github.com/mdn/data.git
mousetrap 1.6.5 Apache-2.0 WITH LLVM-exception git://github.com/ccampbell/mousetrap.git
precond 0.2.3 MIT* https://github.com/MathieuTurcotte/node-precond.git
webcabin-docker 2.2.5 MIT* Unknown
tablesorter 2.31.3 (MIT OR GPL-2.0) git://github.com/Mottie/tablesorter.git
335 dependencies listed.
345 dependencies listed.
+80
View File
@@ -107,6 +107,86 @@ properties of the geometries directly in map, just click the specific geometry:
- *Internet access:* An internet connection is required for the Geometry Viewer
to function correctly.
**JSON Data Editor**
A built in json editor is provided for *JSON/JSONB Data*.Double clicking on json/jsonb
data type cell in data grid will open JSON Editor.Editor provides different mode to
view and edit json data.
.. image:: images/json_editor_modes.png
:alt: Json Editor
:align: center
*Code Mode*:
Provides way to format & compact json data.Also provides ability to repair json data
by fixing quotes and escape characters, removing comments and JSONP notation and
turn JavaScript objects into JSON.
*Tree Mode*:
Enabled to change, add, move, remove, and duplicate fields and values.Provides ability
to searh & hilight data.
*Form Mode*:
Allows only to edit values in json data there by providing ability to keep data structure
unchanged while editing.
*Preview Mode*:
Provides ability to check data before saving and also shows size of current json data.
Format and compact json data as well.
*Editor Toolbar*
Different options are provided to manipulate json data.
Code/Preview mode:
.. image:: images/json_editor_code_preview_tool_bar.png
:alt: Json Editor Code/Preview mode Tool Bar
:align: center
Tree/Form mode:
.. image:: images/json_editor_tree_form_mode_tool_bar.png
:alt: Json Editor Tree/Form mode Tool Bar
:align: center
.. table::
:class: longtable
:widths: 1 3 2
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| Icon | Behavior | Available in mode |
+======================+========================================================================================+===========================+
| *Format Json* | Click to *Format Json* format json data with proper indentation. | Code, Preview |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| *Compact Json* | Click to *Compact Json* get compacted json data. | Code, Preview |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| *Sort* | Click to *Sort* icon to set sorting criteria for the data using file values. | Code, Preview, Tree |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| *Transform* | Click to *Transform* to filter data using JSME query language. | Code, Preview, Tree |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| *Undo* | Click to *Undo* to undo last action performed on data . | Code, Preview, Tree, From |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| *Redo* | Click to *Redo* to repat last action performed on data . | Code, Preview, Tree, From |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| *Mode* | Click to *Mode* dropdown to change dipaly mode of editor.Different modes avaialble | Code, Tree, From, Preview |
| | are Code, Preview, Tree, From. | |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| *Expand All* | Click to *Expand All* to expand json data. | Tree, From |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| *Collapse All* | Click to *Redo* to collapse json data. | Tree, From |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
| *Search Box* | Enter partial/complete string to search in data. | Tree, From |
+----------------------+----------------------------------------------------------------------------------------+---------------------------+
Sort/Filter options dialog
**************************
+1
View File
@@ -35,6 +35,7 @@ Mode is pre-configured for security.
login
user_management
change_user_password
restore_locked_user
ldap
kerberos
oauth2
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 53 KiB

+11 -4
View File
@@ -10,16 +10,16 @@ Use the *Login* dialog to log in to pgAdmin:
:alt: pgAdmin login dialog
:align: center
Use the fields in the *Login* dialog to authenticate your connection. There are
Use the fields in the *Login* dialog to authenticate your connection. There are
two ways to authenticate your connection:
- From pgAdmin version 4.21 onwards, support for LDAP authentication
has been added. If LDAP authentication has been enabled for your pgAdmin
application, you can use your LDAP credentials to log in to pgAdmin:
* Provide the LDAP username in the *Email Address/Username* field.
* Provide the LDAP username in the *Email Address/Username* field.
* Provide your LDAP password in the Password field.
* Provide your LDAP password in the Password field.
- Alternatively, you can use the following information to log in to pgAdmin:
@@ -53,6 +53,13 @@ If you have forgotten the email associated with your account, please contact
your administrator.
Please note that your LDAP password cannot be recovered using this dialog. If
you enter your LDAP username in the *Email Address/Username* field, and then
you enter your LDAP username in the *Email Address/Username* field, and then
enter your email to recover your password, an error message will be displayed
asking you to contact the LDAP administrator to recover your LDAP password.
Avoiding a bruteforce attack
****************************
You have the possibility to lock an account by setting ``MAX_LOGIN_ATTEMPTS``
once it has reached the maximum number of login attempts.
You can disable this feature by setting the value to zero.
+10 -7
View File
@@ -353,6 +353,10 @@ Use the fields on the *Options* panel to manage editor preferences.
* When the *Auto-Rollback?* switch is set to *True*, failed queries are rolled
back.
* When the *Copy SQL from main window to query tool?* switch is set to *True*,
the CREATE sql of the selected object will be copied to query tool when query tool
will open.
* When the *Prompt to save unsaved data changes?* switch is set to *True*, the
editor will prompt the user to saved unsaved data when exiting the data
editor.
@@ -377,14 +381,13 @@ Use the fields on the *Options* panel to manage editor preferences.
Use the fields on the *Results grid* panel to specify your formatting
preferences for copied data.
* Specify the maximum width of the column when 'Resize by data?' is set to True.
If it is set to 0 then columns will auto-size to the maximum width of the data
in the column. If 'Resize by data?' is set to False then this setting won't
take any effect.
* When the *Resize by data?* switch is set to *True*, then data columns will
* When the *Columns sized by* is set to *Column data*, then data columns will
auto-size to the maximum width of the data in the column as loaded in the
first batch. If False, the column will be sized to the widest of the data
type or column name.
first batch. If set to *Column name*, the column will be sized to the widest
of the data type or column name.
* Specify the maximum width of the column in pixels when 'Columns sized by' is
set to *Column data*. If 'Columns sized by' is set to *Column name* then this
setting won't have any effect.
* Use the *Result copy field separator* drop-down listbox to select the field
separator for copied data.
* Use the *Result copy quote character* drop-down listbox to select the quote
+1
View File
@@ -11,6 +11,7 @@ notes for it.
.. toctree::
:maxdepth: 1
release_notes_5_6
release_notes_5_5
release_notes_5_4
release_notes_5_3
+33
View File
@@ -0,0 +1,33 @@
************
Version 5.6
************
Release date: 2021-08-12
This release contains a number of bug fixes and new features since the release of pgAdmin4 5.5.
New features
************
| `Issue #4904 <https://redmine.postgresql.org/issues/4904>`_ - Added support to copy SQL from main window to query tool.
| `Issue #5198 <https://redmine.postgresql.org/issues/5198>`_ - Added support for formatted JSON viewer/editor when interacting with data in a JSON column.
Housekeeping
************
| `Issue #6622 <https://redmine.postgresql.org/issues/6622>`_ - Rename the "Resize by data?" to "Columns sized by" and disabled the 'Maximum column width' button if 'Columns sized by' is set to 'Column data'.
Bug fixes
*********
| `Issue #6337 <https://redmine.postgresql.org/issues/6337>`_ - Ensure that the login account should be locked after N number of attempts. N is configurable using the 'MAX_LOGIN_ATTEMPTS' parameter.
| `Issue #6369 <https://redmine.postgresql.org/issues/6369>`_ - Fixed CSRF errors for stale sessions by increasing the session expiration time for desktop mode.
| `Issue #6448 <https://redmine.postgresql.org/issues/6448>`_ - Fixed an issue in the search object when searching in 'all types' or 'subscription' if the user doesn't have access to the subscription.
| `Issue #6574 <https://redmine.postgresql.org/issues/6574>`_ - Fixed an issue where paste is not working through Right-Click option on PSQL.
| `Issue #6580 <https://redmine.postgresql.org/issues/6580>`_ - Fixed TypeError 'NoneType' object is not sub scriptable.
| `Issue #6586 <https://redmine.postgresql.org/issues/6586>`_ - Fixed incorrect tablespace options in the drop-down for move objects dialog.
| `Issue #6618 <https://redmine.postgresql.org/issues/6618>`_ - Fixed an issue where the titles in query tabs are different.
| `Issue #6619 <https://redmine.postgresql.org/issues/6619>`_ - Fixed incorrect binary path issue when the user deletes the binary path from the preferences.
| `Issue #6643 <https://redmine.postgresql.org/issues/6643>`_ - Ensure that all the required options should be loaded when the Range data type is selected while creating a custom data type.
| `Issue #6650 <https://redmine.postgresql.org/issues/6650>`_ - Fixed dashboard server activity issue when active_since parameter is None.
| `Issue #6664 <https://redmine.postgresql.org/issues/6664>`_ - Fixed an issue where even if the user is locked, he can reset the password and can login into pgAdmin.
+50
View File
@@ -0,0 +1,50 @@
.. _restore_locked_user:
*****************************
`Lock/Restore Account`:index:
*****************************
Account locking/unlocking via user management dialog:
An admin can lock and unlock user from the user management dialog. This allows the admin to lock or restore
the user when there are several failed login attempts. This page guides you through configuring per-user
locking/restoring. For more details visit :ref:`User management <user_management>`.
.. csv-table::
:header: "**Parameter**", "**Description**"
:class: longtable
:widths: 35, 55
"MAX_LOGIN_ATTEMPTS", "Which sets the number of failed login that
are allowed. If this value is exceeded, the account is locked and can be reset by an administrator.
By setting the variable to the value zero this feature is deactivated."
When Administrator itself gets locked, following steps may be considered to restore it:
- Increase MAX_LOGIN_ATTEMPTS, and try to successfully login to restore the account.
OR
By updating SQLite DB (pgAdmin4.db):
* Locate the pgAdmin4.db file and open it using any DB Browser (or DB Browser for SQLite)
* After opening the DB file, head towards 'Execute SQL' section.
* Run below query -
*UPDATE USER SET LOCKED = false, LOGIN_ATTEMPTS = 0 WHERE USERNAME = <YOUR_EMAIL_ID>*
* Make sure the query changes are committed.
Account locking by failed login attempts:
.. image:: images/login_attempt_user.png
:alt: Failed login attempt for current user
:align: center
pgAdmin application is configured to lock a user account when a number of consecutive failed login attempts are
exceeded.
MAX_LOGIN_ATTEMPTS is defaulted to 3 unsuccessful login attempts, after which the account would be locked.
The only way to restore the user account is by contacting the Administrator and ask to unlock it.
+2
View File
@@ -58,6 +58,8 @@ Provide information about the new pgAdmin role in the row:
as authentication source since LDAP password is not stored in the pgAdmin database.
* Re-enter the password in the *Confirm password* field. This field is disabled
if you select *ldap* as authentication source.
* Move the *Locked* switch to the *True* position if you want to lock the account;
the default is *False*. This functionality is useful when a user is locked by trying unsuccessful login attempts.
To discard a user, and revoke access to pgAdmin, click the trash icon to the
left of the row and confirm deletion in the *Delete user?* dialog.
+3
View File
@@ -5,6 +5,9 @@ if [ "$EUID" -ne 0 ]
exit 1
fi
echo "Running apt update..."
apt update
echo "Installing system pre-requisites..."
apt install -y curl apt-transport-https ca-certificates gnupg
+18 -2
View File
@@ -43,7 +43,7 @@ APP_ICON = 'pg-icon'
# Application version number components
APP_RELEASE = 5
APP_REVISION = 5
APP_REVISION = 6
# Application version suffix, e.g. 'beta1', 'dev'. Usually an empty string
# for GA releases.
@@ -52,7 +52,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
APP_VERSION_INT = 50500
APP_VERSION_INT = 50600
# DO NOT CHANGE!
# The application version string, constructed from the components
@@ -513,6 +513,10 @@ SESSION_SKIP_PATHS = [
# expire after the specified number of *days*.
SESSION_EXPIRATION_TIME = 1
# Make SESSION_EXPIRATION_TIME to 1 week in DESKTOP mode
if not SERVER_MODE:
SESSION_EXPIRATION_TIME = 7
# CHECK_SESSION_FILES_INTERVAL is interval in Hours. Application will check
# the session files for cleanup after specified number of *hours*.
CHECK_SESSION_FILES_INTERVAL = 24
@@ -570,6 +574,18 @@ ENHANCED_COOKIE_PROTECTION = True
AUTHENTICATION_SOURCES = ['internal']
##########################################################################
# MAX_LOGIN_ATTEMPTS which sets the number of failed login attempts that
# are allowed. If this value is exceeded the account is locked and can be
# reset by an administrator. By setting the variable to the value zero
# this feature is deactivated.
##########################################################################
MAX_LOGIN_ATTEMPTS = 3
##########################################################################
# Only consider password to check the failed login attempts, email is
# excluded from this check
LOGIN_ATTEMPT_FIELDS = ['password']
##########################################################################
# LDAP Configuration
##########################################################################
+33
View File
@@ -0,0 +1,33 @@
"""empty message
Revision ID: 6650c52670c2
Revises: c465fee44968
Create Date: 2021-07-10 18:12:38.821602
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
from pgadmin import db
revision = '6650c52670c2'
down_revision = 'c465fee44968'
branch_labels = None
depends_on = None
def upgrade():
db.engine.execute(
'ALTER TABLE user ADD COLUMN locked BOOLEAN DEFAULT FALSE'
)
db.engine.execute(
'ALTER TABLE user ADD COLUMN login_attempts int DEFAULT 0'
)
def downgrade():
# pgAdmin only upgrades, downgrade not implemented.
pass
+8 -1
View File
@@ -7,6 +7,7 @@ Create Date: 2021-06-04 14:42:12.843116
"""
from pgadmin.model import db, User
from sqlalchemy.sql import text
import uuid
@@ -42,7 +43,13 @@ def upgrade():
'confirmed_at, masterpass_check, auth_source '
'from user_old')
db.engine.execute(User.__table__.insert(), [
statement = text("""
INSERT INTO user(id, username, email, password, active,
confirmed_at, masterpass_check, auth_source, fs_uniquifier)
VALUES(:id, :username, :email, :password, :active, :confirmed_at,
:masterpass_check, :auth_source, :fs_uniquifier)""")
db.engine.execute(statement, [
{
**row,
'fs_uniquifier': uuid.uuid4().hex
+1
View File
@@ -100,6 +100,7 @@
"jquery-contextmenu": "^2.9.2",
"jquery-ui": "^1.12.1",
"json-bignumber": "^1.0.1",
"jsoneditor": "^9.5.1",
"karma-coverage": "^2.0.3",
"leaflet": "^1.5.1",
"lodash": "4.*",
+4 -5
View File
@@ -397,8 +397,7 @@ def create_app(app_name=None):
# If version not available, user must have aborted. Tables are not
# created and so its an empty db
version = get_version()
if not os.path.exists(SQLITE_PATH) or version == -1:
if not os.path.exists(SQLITE_PATH) or get_version() == -1:
# If running in cli mode then don't try to upgrade, just raise
# the exception
if not cli_mode:
@@ -681,15 +680,15 @@ def create_app(app_name=None):
from pgadmin.utils.driver import get_driver
from flask import current_app
# remove key
current_app.keyManager.reset()
for mdl in current_app.logout_hooks:
try:
mdl.on_logout(user)
except Exception as e:
current_app.logger.exception(e)
# remove key
current_app.keyManager.reset()
_driver = get_driver(PG_DEFAULT_DRIVER)
_driver.gc_own()
+44 -5
View File
@@ -12,18 +12,18 @@
import config
import copy
from flask import current_app, flash, Response, request, url_for,\
from flask import current_app, flash, Response, request, url_for, \
session, redirect
from flask_babelex import gettext
from flask_security.views import _security
from flask_security.utils import get_post_logout_redirect, \
get_post_login_redirect
get_post_login_redirect, logout_user
from pgadmin import db, User
from pgadmin.utils import PgAdminModule
from pgadmin.utils.constants import KERBEROS, INTERNAL, OAUTH2, LDAP
from pgadmin.authenticate.registry import AuthSourceRegistry
MODULE_NAME = 'authenticate'
auth_obj = None
@@ -46,16 +46,49 @@ def login():
auth_obj = AuthSourceManager(form, copy.deepcopy(
config.AUTHENTICATION_SOURCES))
if OAUTH2 in config.AUTHENTICATION_SOURCES\
if OAUTH2 in config.AUTHENTICATION_SOURCES \
and 'oauth2_button' in request.form:
session['auth_obj'] = auth_obj
session['auth_source_manager'] = None
username = form.data['email']
user = User.query.filter_by(username=username,
auth_source=INTERNAL).first()
if user:
if user.login_attempts >= config.MAX_LOGIN_ATTEMPTS > 0:
user.locked = True
else:
user.locked = False
db.session.commit()
if user.login_attempts >= config.MAX_LOGIN_ATTEMPTS > 0:
flash(gettext('Your account is locked. Please contact the '
'Administrator.'),
'warning')
logout_user()
return redirect(get_post_logout_redirect())
# Validate the user
if not auth_obj.validate():
for field in form.errors:
flash_login_attempt_error = None
if user and field in config.LOGIN_ATTEMPT_FIELDS:
if config.MAX_LOGIN_ATTEMPTS > 0:
user.login_attempts += 1
left_attempts = \
config.MAX_LOGIN_ATTEMPTS - user.login_attempts
flash_login_attempt_error = \
gettext('You are left with {0} more attempts.'.
format(left_attempts))
db.session.commit()
for error in form.errors[field]:
flash(error, 'warning')
if flash_login_attempt_error:
flash(flash_login_attempt_error, 'warning')
return redirect(get_post_logout_redirect())
# Authenticate the user
@@ -66,14 +99,20 @@ def login():
current_auth_obj = auth_obj.as_dict()
if not status:
if current_auth_obj['current_source'] ==\
if current_auth_obj['current_source'] == \
KERBEROS:
return redirect('{0}?next={1}'.format(url_for(
'authenticate.kerberos_login'), url_for('browser.index')))
flash(msg, 'danger')
return redirect(get_post_logout_redirect())
session['auth_source_manager'] = current_auth_obj
if user:
user.login_attempts = 0
db.session.commit()
if 'auth_obj' in session:
session.pop('auth_obj')
return redirect(get_post_login_redirect())
+8 -1
View File
@@ -31,7 +31,8 @@ from flask_security.recoverable import reset_password_token_status, \
generate_reset_password_token, update_password
from flask_security.signals import reset_password_instructions_sent
from flask_security.utils import config_value, do_flash, get_url, \
get_message, slash_url_suffix, login_user, send_mail, logout_user
get_message, slash_url_suffix, login_user, send_mail, logout_user, \
get_post_logout_redirect
from flask_security.views import _security, view_commit, _ctx
from werkzeug.datastructures import MultiDict
@@ -1329,6 +1330,12 @@ if hasattr(config, 'SECURITY_RECOVERABLE') and config.SECURITY_RECOVERABLE:
auth_obj = AuthSourceManager(form, [INTERNAL])
session['_auth_source_manager_obj'] = auth_obj.as_dict()
if user.login_attempts >= config.MAX_LOGIN_ATTEMPTS > 0:
flash(gettext('You successfully reset your password but'
' your account is locked. Please contact '
'the Administrator.'),
'warning')
return redirect(get_post_logout_redirect())
do_flash(*get_message('PASSWORD_RESET'))
login_user(user)
auth_obj = AuthSourceManager(form, [INTERNAL])
@@ -823,9 +823,9 @@ class TableView(BaseTableView, DataTypeReader, SchemaDiffTableCompare):
if 'relacl' in data:
data['relacl'] = parse_priv_to_db(data['relacl'], self.acl)
# Parse & format columns
data = column_utils.parse_format_columns(data)
data = TableView.check_and_convert_name_to_string(data)
# Parse & format columns
data = column_utils.parse_format_columns(data)
data = TableView.check_and_convert_name_to_string(data)
# 'coll_inherits' is Array but it comes as string from browser
# We will convert it again to list
@@ -1473,7 +1473,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable, VacuumSettings):
else:
error, errmsg = BaseTableView._check_for_create_sql(data)
if error:
return gettext('-- definition incomplete')
return gettext('-- definition incomplete'), data['name']
# validate constraint data.
self._validate_constraint_data(data)
@@ -579,18 +579,18 @@ define('pgadmin.node.type', [
},{
id: 'opcname', label: gettext('Subtype operator class'), cell: 'string',
mode: ['properties', 'create', 'edit'], group: gettext('Range Type'),
disabled: 'inSchema', readonly: 'inEditMode', deps: ['typname'],
control: 'select', options: function() {
var l_typname = this.model.get('typname'),
self = this,
disabled: 'inSchema', readonly: 'inEditMode', deps: ['typname'],
control: 'select2', options: function(control) {
var l_typname = control.model.get('typname'),
result = [];
if(!_.isUndefined(l_typname) && l_typname != '')
{
var node = this.field.get('schema_node'),
var node = control.field.get('schema_node'),
_url = node.generate_url.apply(
node, [
null, 'get_subopclass', this.field.get('node_data'), false,
this.field.get('node_info'),
null, 'get_subopclass', control.field.get('node_data'), false,
control.field.get('node_info'),
]);
$.ajax({
async: false,
@@ -602,7 +602,7 @@ define('pgadmin.node.type', [
result = res.data;
})
.fail(function() {
self.model.trigger('pgadmin:view:fetch:error', self.model, self.field);
control.model.trigger('pgadmin:view:fetch:error', control.model, control.field);
});
//
}
@@ -640,6 +640,11 @@ define('pgadmin.node.type', [
});
}
// If is_collate is true then do not disable
if(!is_collate) {
m.set({'collname': '', silent: true});
this.options = [];
}
return is_collate ? false : true;
},
},{
@@ -647,18 +652,17 @@ define('pgadmin.node.type', [
type: 'text', mode: ['properties', 'create', 'edit'],
group: gettext('Range Type'),
disabled: 'inSchema', readonly: 'inEditMode', deps: ['name', 'typname'],
control: 'select', options: function() {
var name = this.model.get('name'),
self = this,
control: 'select2', options: function(control) {
var name = control.model.get('name'),
result = [];
if(!_.isUndefined(name) && name != '')
{
var node = this.field.get('schema_node'),
var node = control.field.get('schema_node'),
_url = node.generate_url.apply(
node, [
null, 'get_canonical', this.field.get('node_data'), false,
this.field.get('node_info'),
null, 'get_canonical', control.field.get('node_data'), false,
control.field.get('node_info'),
]);
$.ajax({
async: false,
@@ -670,8 +674,7 @@ define('pgadmin.node.type', [
result = res.data;
})
.fail(function() {
self.model.trigger('pgadmin:view:fetch:error',
self.model, self.field);
control.model.trigger('pgadmin:view:fetch:error', control.model, control.field);
});
}
return result;
@@ -680,21 +683,22 @@ define('pgadmin.node.type', [
id: 'rngsubdiff', label: gettext('Subtype diff function'), cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit'],
group: gettext('Range Type'),
disabled: 'inSchema', readonly: 'inEditMode', deps: ['opcname'],
control: 'select', options: function() {
var l_typname = this.model.get('typname'),
l_opcname = this.model.get('opcname'),
self = this,
disabled: 'inSchema', readonly: 'inEditMode', deps: ['typname', 'opcname'],
control: 'select2', options: function(control) {
var l_typname = control.model.get('typname'),
l_opcname = control.model.get('opcname'),
result = [];
this.options = [];
control.model.set({'rngsubdiff': [], silent: true});
if(!_.isUndefined(l_typname) && l_typname != '' &&
!_.isUndefined(l_opcname) && l_opcname != '') {
var node = this.field.get('schema_node'),
var node = control.field.get('schema_node'),
_url = node.generate_url.apply(
node, [
null, 'get_stypediff',
this.field.get('node_data'), false,
this.field.get('node_info'),
control.field.get('node_data'), false,
control.field.get('node_info'),
]);
$.ajax({
async: false,
@@ -706,8 +710,7 @@ define('pgadmin.node.type', [
result = res.data;
})
.fail(function() {
self.model.trigger('pgadmin:view:fetch:error',
self.model, self.field);
control.model.trigger('pgadmin:view:fetch:error', control.model, control.field);
});
}
return result;
@@ -29,11 +29,16 @@ SELECT rngsubtype, st.typname,
rngcollation,
CASE WHEN n.nspname IS NOT NULL THEN pg_catalog.concat(pg_catalog.quote_ident(n.nspname), '.', pg_catalog.quote_ident(col.collname)) ELSE col.collname END AS collname,
rngsubopc, opc.opcname,
rngcanonical, rngsubdiff
rngcanonical, rngsubdiff as rngsubdiff_proc,
CASE WHEN length(ns.nspname::text) > 0 AND length(pgpr.proname::text) > 0 THEN
pg_catalog.concat(quote_ident(ns.nspname), '.', pg_catalog.quote_ident(pgpr.proname))
ELSE '' END AS rngsubdiff
FROM pg_catalog.pg_range
LEFT JOIN pg_catalog.pg_type st ON st.oid=rngsubtype
LEFT JOIN pg_catalog.pg_collation col ON col.oid=rngcollation
LEFT JOIN pg_catalog.pg_namespace n ON col.collnamespace=n.oid
LEFT JOIN pg_catalog.pg_opclass opc ON opc.oid=rngsubopc
LEFT JOIN pg_catalog.pg_proc pgpr ON pgpr.oid = rngsubdiff
LEFT JOIN pg_catalog.pg_namespace ns ON ns.oid=pgpr.pronamespace
WHERE rngtypid={{tid}}::oid;
{% endif %}
@@ -114,12 +114,16 @@ define('pgadmin.node.tablespace', [
obj_type: 'all',
user: undefined,
},
initialize: function(attrs, otherArgs) {
this.selected_ts = otherArgs.selected_ts;
Backbone.Model.prototype.initialize.apply(this, [attrs, otherArgs]);
},
schema: [{
id: 'tblspc', label: gettext('New tablespace'),
type: 'text', disabled: false, control: 'node-list-by-name',
node: 'tablespace', select2: {allowClear: false},
filter: function(o) {
return o && (o.label != d.label);
return o && (o.label != this.model.selected_ts);
},
},{
id: 'obj_type', label: gettext('Object type'),
@@ -148,7 +152,7 @@ define('pgadmin.node.tablespace', [
var self = this,
sql_tab_args = self.model.toJSON();
// Add existing tablespace
sql_tab_args.old_tblspc = d.label;
sql_tab_args.old_tblspc = this.model.selected_ts;
// Fetches modified SQL
$.ajax({
@@ -236,7 +240,7 @@ define('pgadmin.node.tablespace', [
// Create treeInfo
var treeInfo = node.getTreeNodeHierarchy.apply(node, [_i]);
// Instance of backbone model
var newModel = new objModel({}, {node_info: treeInfo}),
var newModel = new objModel({}, {node_info: treeInfo, selected_ts: _d.label}),
fields = Backform.generateViewSchema(
treeInfo, newModel, 'create', node,
treeInfo.server, true
@@ -36,7 +36,7 @@ class SharedServersGetTestCase(BaseTestGenerator):
if config.SERVER_MODE is False:
self.skipTest(
"Can not run shared servers test cases in the SERVER mode."
"Can not run shared servers test cases in the Desktop mode."
)
self.server['shared'] = True
url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
@@ -192,7 +192,8 @@ class ServerType(object):
for bin_path in bin_path_json:
if int(bin_path['version']) <= sverison < \
int(bin_path['next_major_version']) and \
bin_path['binaryPath'] is not None:
bin_path['binaryPath'] is not None and \
bin_path['binaryPath'].strip() != '':
return bin_path['binaryPath']
if bin_path['isDefault']:
+12 -1
View File
@@ -902,6 +902,7 @@ define('pgadmin.browser.node', [
// Callback to render query editor
show_query_tool: function(args, item) {
var preference = pgBrowser.get_preference('sqleditor', 'copy_sql_to_query_tool');
var t = pgBrowser.tree,
i = item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
@@ -910,7 +911,17 @@ define('pgadmin.browser.node', [
return;
// Here call data grid method to render query tool
pgAdmin.DataGrid.show_query_tool('', i);
//Open query tool with create script if copy_sql_to_query_tool is true else open blank query tool
if(preference.value && !d._type.includes('coll-')){
var stype = d._type.toLowerCase();
var data = {
'script': stype,
data_disabled: gettext('The selected tree node does not support this option.'),
};
pgBrowser.Node.callbacks.show_script(data);
}else{
pgAdmin.DataGrid.show_query_tool('', i);
}
},
// Callback to render psql tool.
@@ -0,0 +1,43 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from pgadmin.utils.route import BaseTestGenerator
import config
from unittest.mock import patch
if not config.SERVER_MODE:
MODE = 'Desktop Mode'
SESSION_EXP_TIME_CONSTANT = 7
else:
MODE = 'Server Mode'
SESSION_EXP_TIME_CONSTANT = 1
class SetSessionExpirationTimeTestCase(BaseTestGenerator):
"""
This class verifies whether session expire time has been appropriately
set to desktop & server mode respectively.
"""
scenarios = [
(
'TestCase for verifying session expire time is set to {0} '
'days for {1}'.format(SESSION_EXP_TIME_CONSTANT, MODE),
dict(
session_expiration_time=SESSION_EXP_TIME_CONSTANT
))
]
@patch('config.SESSION_EXPIRATION_TIME',
side_effect=SESSION_EXP_TIME_CONSTANT)
def runTest(self, mock_session_expiration_time):
self.assertEqual(
self.session_expiration_time,
config.SESSION_EXPIRATION_TIME.side_effect)
+9 -9
View File
@@ -365,15 +365,15 @@ def get_long_running_query_status(activities):
for row in activities:
row['row_type'] = None
# We care for only those queries which are in active state.
if row['state'] != 'active':
continue
active_since = float(row['active_since'])
if active_since > warning_value:
row['row_type'] = 'warning'
if active_since > alert_value:
row['row_type'] = 'alert'
# We care for only those queries which are in active state and
# have active_since parameter and not None
if row['state'] == 'active' and 'active_since' in row and \
row['active_since'] is not None:
active_since = float(row['active_since'])
if active_since > warning_value:
row['row_type'] = 'warning'
if active_since > alert_value:
row['row_type'] = 'alert'
@blueprint.route('/dashboard_stats',
@@ -0,0 +1,153 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import random
import time
from regression.feature_utils.base_feature_test import BaseFeatureTest
from regression.python_test_utils import test_utils
from regression.feature_utils.tree_area_locators import TreeAreaLocators
from regression.feature_utils.locators import NavMenuLocators, \
QueryToolLocators
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
class CopySQLFeatureTest(BaseFeatureTest):
""" This class test acceptance test scenarios """
scenarios = [
("Test copy sql to query tool", dict())
]
test_table_name = ""
def before(self):
self.page.add_server(self.server)
def runTest(self):
self._update_preferences_setting()
self._create_table()
sql_query = self._get_sql_query()
query_tool_result = self._get_query_tool_result()
self.assertEqual(query_tool_result, sql_query)
def after(self):
self.page.remove_server(self.server)
test_utils.delete_table(
self.server, self.test_db, self.test_table_name)
def _get_sql_query(self):
self.page.click_tab("SQL")
# Wait till data is displayed in SQL Tab
self.assertTrue(self.page.check_if_element_exist_by_xpath(
"//*[contains(@class,'CodeMirror-lines') and "
"contains(.,'CREATE TABLE IF NOT EXISTS public.%s')]"
% self.test_table_name, 10), "No data displayed in SQL tab")
# Fetch the inner html & check for escaped characters
source_code = self.driver.find_elements_by_xpath(
QueryToolLocators.code_mirror_data_xpath)
sql_query = ''
for data in source_code:
sql_query += data.text
sql_query += '\n'
return sql_query
def _get_query_tool_result(self):
self.page.open_query_tool()
self.page.wait_for_spinner_to_disappear()
time.sleep(5)
self.driver.switch_to.default_content()
self.driver.switch_to_frame(
self.driver.find_element_by_tag_name("iframe"))
code_mirror = self.driver.find_elements_by_xpath(
QueryToolLocators.code_mirror_data_xpath)
query_tool_result = ''
for data in code_mirror:
query_tool_result += data.text
query_tool_result += '\n'
return query_tool_result
def _create_table(self):
self.test_table_name = "test_table" + str(random.randint(1000, 3000))
test_utils.create_table(self.server, self.test_db,
self.test_table_name)
self.page.expand_database_node(
self.server['name'],
self.server['db_password'], self.test_db)
self.page.toggle_open_tables_node(
self.server['name'], self.server['db_password'],
self.test_db, 'public')
self.page.click_a_tree_node(
self.test_table_name,
TreeAreaLocators.sub_nodes_of_tables_node)
def _update_preferences_setting(self):
file_menu = self.page.find_by_css_selector(
NavMenuLocators.file_menu_css)
file_menu.click()
self.page.retry_click(
(By.CSS_SELECTOR, NavMenuLocators.preference_menu_item_css),
(By.XPATH, NavMenuLocators.specified_preference_tree_node
.format('Browser'))
)
wait = WebDriverWait(self.page.driver, 10)
browser_node = self.page.find_by_xpath(
NavMenuLocators.specified_preference_tree_node.format('Browser'))
if self.page.find_by_xpath(
NavMenuLocators.specified_pref_node_exp_status.
format('Browser')).get_attribute('aria-expanded') == 'false':
ActionChains(self.driver).double_click(browser_node).perform()
self.page.retry_click(
(By.XPATH, NavMenuLocators.specified_sub_node_of_pref_tree_node.
format('Browser', 'Display')),
(By.XPATH, NavMenuLocators.show_system_objects_pref_label_xpath))
# Wait till the preference dialogue box is displayed by checking the
# visibility of Show System Object label
wait.until(EC.presence_of_element_located(
(By.XPATH, NavMenuLocators.show_system_objects_pref_label_xpath))
)
specified_preference_tree_node_name = 'Query Tool'
sql_editor = self.page.find_by_xpath(
NavMenuLocators.specified_preference_tree_node.format(
specified_preference_tree_node_name))
if self.page.find_by_xpath(
NavMenuLocators.specified_pref_node_exp_status.
format(specified_preference_tree_node_name)).get_attribute(
'aria-expanded') == 'false':
ActionChains(self.driver).double_click(sql_editor).perform()
option_node = self.page.find_by_xpath(
NavMenuLocators.specified_sub_node_of_pref_tree_node.format(
specified_preference_tree_node_name, 'Options'))
option_node.click()
self.page.set_switch_box_status(
NavMenuLocators.copy_sql_to_query_tool_switch_btn, 'Yes')
# save and close the preference dialog.
self.page.click_modal('Save')
@@ -241,13 +241,34 @@ CREATE TABLE public.nonintpkey
else:
ActionChains(self.driver).send_keys(value). \
send_keys(Keys.ENTER).perform()
elif cell_type in ['text', 'json', 'text[]', 'boolean[]']:
elif cell_type in ['text', 'text[]', 'boolean[]']:
text_area_ele = self.page.find_by_css_selector(
QueryToolLocators.row_editor_text_area_css)
text_area_ele.clear()
text_area_ele.click()
text_area_ele.send_keys(value)
# Click on editor's Save button
self.page.find_by_css_selector(
QueryToolLocators.text_editor_ok_btn_css).click()
elif cell_type in ['json', 'jsonb']:
jsoneditor_area_ele = self.page.find_by_css_selector(
QueryToolLocators.json_editor_text_area_css)
if not value == "":
actions = ActionChains(self.driver)
platform = 'mac'
if "platform" in self.driver.capabilities:
platform = (self.driver.capabilities["platform"]).lower()
elif "platformName" in self.driver.capabilities:
platform = \
(self.driver.capabilities["platformName"]).lower()
if 'mac' in platform:
key_to_press = Keys.COMMAND
else:
key_to_press = Keys.CONTROL
ActionChains(self.driver).key_down(key_to_press)\
.send_keys('a').key_up(key_to_press)\
.send_keys(Keys.DELETE).perform()
ActionChains(self.driver).send_keys(value) .perform()
# Click on editor's Save button
self.page.find_by_css_selector(
QueryToolLocators.text_editor_ok_btn_css).click()
+327 -308
View File
File diff suppressed because it is too large. Load diff
+3 -1
View File
@@ -30,7 +30,7 @@ import uuid
#
##########################################################################
SCHEMA_VERSION = 30
SCHEMA_VERSION = 31
##########################################################################
#
@@ -80,6 +80,8 @@ class User(db.Model, UserMixin):
# fs_uniquifier is required by flask-security-too >= 4.
fs_uniquifier = db.Column(db.String(255), unique=True, nullable=False,
default=(lambda _: uuid.uuid4().hex))
login_attempts = db.Column(db.Integer, default=0)
locked = db.Column(db.Boolean(), default=False)
class Setting(db.Model):
@@ -83,7 +83,8 @@ define('pgadmin.preferences', [
});
preferences.on('change', function(m) {
var id = m.get('id');
var id = m.get('id'),
dependents = m.get('dependents');
if (!(id in changed)) {
// Keep track of the original value
changed[id] = m._previousAttributes.value;
@@ -91,8 +92,46 @@ define('pgadmin.preferences', [
// Remove unchanged models.
delete changed[id];
}
// Check dependents exist or not. If exists then call dependentsFound function.
if (!_.isNull(dependents) && Array.isArray(dependents) && dependents.length > 0) {
dependentsFound(m.get('name'), m.get('value'), dependents);
}
});
/*
* Function: dependentsFound
*
* This method will be used to iterate through all the controls and
* dependents. If found then perform the appropriate action.
*/
var dependentsFound = function(pref_name, pref_val, dependents) {
// Iterate through all the controls and check the dependents
_.each(controls, function(c) {
let ctrl_name = c.model.get('name');
_.each(dependents, function(deps) {
if (ctrl_name === deps) {
// Create methods to take appropriate actions and call here.
enableDisableMaxWidth(pref_name, pref_val, c);
}
});
});
};
/*
* Function: enableDisableMaxWidth
*
* This method will be used to enable and disable Maximum Width control
*/
var enableDisableMaxWidth = function(pref_name, pref_val, control) {
if (pref_name === 'column_data_auto_resize' && pref_val === 'by_name') {
control.$el.find('input').prop('disabled', true);
control.$el.find('input').val(0);
} else if (pref_name === 'column_data_auto_resize' && pref_val === 'by_data') {
control.$el.find('input').prop('disabled', false);
}
};
/*
* Function: renderPreferencePanel
*
@@ -144,6 +183,16 @@ define('pgadmin.preferences', [
controls.push(cntr);
});
/* Iterate through all preferences and check if dependents found.
* If found then call the dependentsFound method
*/
_.each(prefs, function(p) {
let m = preferences.get(p.id);
let dependents = m.get('dependents');
if (!_.isNull(dependents) && Array.isArray(dependents) && dependents.length > 0) {
dependentsFound(m.get('name'), m.get('value'), dependents);
}
});
};
/*
@@ -383,6 +432,7 @@ define('pgadmin.preferences', [
'category_id': d.id,
'mid': d.mid,
'name': p.name,
'dependents': p.dependents,
});
/*
* We don't know until now, how to render the control for
+8 -4
View File
@@ -7,7 +7,6 @@
#
##########################################################################
from pgadmin.model import Version
from pgadmin.model import db
@@ -22,9 +21,14 @@ def check_db_tables():
db_table_names = get_db_table_names()
# check table is actually present in the db.
for table_name in db_table_names:
if not db.engine.dialect.has_table(db.engine, table_name):
invalid_tb_names.append(table_name)
is_error = True
try:
if not db.inspect(db.engine).has_table(table_name=table_name):
invalid_tb_names.append(table_name)
is_error = True
except AttributeError:
if not db.engine.dialect.has_table(db.engine, table_name):
invalid_tb_names.append(table_name)
is_error = True
if is_error:
return True, invalid_tb_names
+2
View File
@@ -23,3 +23,5 @@
@import '../vendor/backgrid/backgrid-select-all.css';
@import 'node_modules/xterm/css/xterm.css';
@import 'node_modules/jsoneditor/dist/jsoneditor.min.css';
+114 -41
View File
@@ -44,6 +44,11 @@ import gettext from 'sources/gettext';
return $('<textarea class=\'pg-textarea text-12\' hidefocus rows=5\'>');
}
// return json editor element
function getJsonEditor() {
return $('<div id=\'pg-json-editor\' hidefocus\'>');
}
// Generate and return editor buttons
function getButtons(editable) {
var $buttons = $('<div class=\'pg_buttons\' />'),
@@ -293,15 +298,16 @@ import gettext from 'sources/gettext';
// JSON data type editor
function JsonTextEditor(args) {
var $input, $wrapper, $buttons;
var defaultValue;
var $input, $wrapper, $buttons, $editor;
var defaultValue, tmpdata;
var scope = this;
var editorInitialized = false;
this.init = function() {
var $container = $('body');
$wrapper = getWrapper().appendTo($container);
$input = getTextArea().appendTo($wrapper);
$input = getJsonEditor().appendTo($wrapper);
$buttons = getButtons(true).appendTo($wrapper);
$buttons.find('button:first').on('click', this.cancel);
@@ -309,7 +315,6 @@ import gettext from 'sources/gettext';
$input.on('keydown', this.handleKeyDown);
scope.position(args.position);
$input.trigger('focus').trigger('select');
};
this.handleKeyDown = function(e) {
@@ -318,12 +323,6 @@ import gettext from 'sources/gettext';
} else if (e.which == $.ui.keyCode.ESCAPE) {
e.preventDefault();
scope.cancel();
} else if (e.which == $.ui.keyCode.TAB && e.shiftKey) {
e.preventDefault();
args.grid.navigatePrev();
} else if (e.which == $.ui.keyCode.TAB) {
e.preventDefault();
args.grid.navigateNext();
}
};
@@ -332,7 +331,6 @@ import gettext from 'sources/gettext';
};
this.cancel = function() {
$input.val(defaultValue);
args.cancelChanges();
};
@@ -352,15 +350,24 @@ import gettext from 'sources/gettext';
};
this.destroy = function() {
$editor.destroy();
$wrapper.remove();
};
this.focus = function() {
$input.trigger('focus');
$editor.focus();
};
this.loadValue = function(item) {
var data = defaultValue = item[args.column.field];
/* Can be useful until JSON editor loads */
tmpdata = data;
if (_.isNull(data)){
defaultValue = undefined;
data = undefined;
}
/* If jsonb or array */
if(args.column.column_type_internal === 'jsonb' && !Array.isArray(data) && data != null) {
data = JSONBigNumber.stringify(JSONBigNumber.parse(data), null, 4);
@@ -375,20 +382,60 @@ import gettext from 'sources/gettext';
});
data = '[' + temp.join() + ']';
}
/* if json take as is */
$input.val(data);
$input.trigger('select');
/* if data is string then convert to json*/
if ( data != '' && typeof data === 'string'){
data = JSON.parse(data);
}
/* Create editor if required & set data*/
if ($editor){
$editor.set(data);
$editor.focus();
}else{
editorInitialized = true;
require.ensure(['jsoneditor'], function(require) {
var JSONEditor = require('jsoneditor');
var jsonContainer = document.getElementById('pg-json-editor');
var options = { modes: ['code', 'form', 'tree','preview']};
$editor = new JSONEditor(jsonContainer, options);
$editor.set(data);
$editor.focus();
}, function(error){
throw(error);
}, 'jsoneditorchunk');
}
};
this.serializeValue = function() {
if ($input.val() === '') {
return null;
/* Create editor if data is null/new data entry */
if( !editorInitialized) {
require.ensure(['jsoneditor'], function(require) {
var JSONEditor = require('jsoneditor');
var jsonContainer = document.getElementById('pg-json-editor');
var options = {modes: ['code', 'form', 'tree','preview']};
if(jsonContainer) {
$editor = new JSONEditor(jsonContainer, options);
$editor.focus();
return null;
}
}, function(error){
throw(error);
}, 'jsoneditorchunk');}
if($editor){
let data = $editor.getText();
if (data === '') {
return null;
}
return data;
}else{
// The loader is not loaded yet ?
return tmpdata;
}
return $input.val();
};
this.applyValue = function(item, state){
if(args.column.column_type_internal === 'jsonb') {
if(args.column.column_type_internal === 'jsonb' || args.column.column_type_internal === 'json') {
setValue(args, item, state, 'jsonb');
} else {
setValue(args, item, state, 'text');
@@ -396,19 +443,24 @@ import gettext from 'sources/gettext';
};
this.isValueChanged = function() {
if ($input.val() == '' && _.isUndefined(defaultValue)) {
let data = $editor.getText();
if (data == '' && (_.isUndefined(defaultValue) || _.isNull(defaultValue) )) {
return false;
} else {
return (!($input.val() == '' && _.isNull(defaultValue))) && ($input.val() != defaultValue);
if(! _.isUndefined(defaultValue) && defaultValue != ''){
defaultValue = JSON.stringify(JSON.parse(defaultValue), null,2);
}
return (!( data == '' && _.isNull(defaultValue)) && (data != defaultValue));
}
};
this.validate = function() {
if(args.column.column_type_internal === 'jsonb' ||
args.column.column_type_internal === 'json') {
let data = $editor.getText();
try {
if($input.val() != ''){
JSON.parse($input.val());
if(data != ''){
JSON.parse(data);
}
} catch(e) {
$input.addClass('pg-text-invalid');
@@ -418,7 +470,6 @@ import gettext from 'sources/gettext';
};
}
}
$input.removeClass('pg-text-invalid');
return {
valid: true,
msg: null,
@@ -529,22 +580,22 @@ import gettext from 'sources/gettext';
// JSON data type editor
function ReadOnlyJsonTextEditor(args) {
var $input, $wrapper, $buttons;
var $input, $wrapper, $buttons, $editor;
var defaultValue;
var scope = this;
var tmpdata;
this.init = function() {
var $container = $('body');
$wrapper = getWrapper().appendTo($container);
$input = getTextArea().appendTo($wrapper);
$input = getJsonEditor().appendTo($wrapper);
$buttons = getButtons(false).appendTo($wrapper);
$buttons.find('button:first').on('click', this.cancel);
$input.on('keydown', this.handleKeyDown);
scope.position(args.position);
$input.trigger('focus').trigger('select');
};
this.handleKeyDown = function(e) {
@@ -553,14 +604,6 @@ import gettext from 'sources/gettext';
} else if (e.which == $.ui.keyCode.ESCAPE) {
e.preventDefault();
scope.cancel();
} else if (e.which == $.ui.keyCode.TAB && e.shiftKey) {
scope.cancel();
e.preventDefault();
args.grid.navigatePrev();
} else if (e.which == $.ui.keyCode.TAB) {
scope.cancel();
e.preventDefault();
args.grid.navigateNext();
}
};
@@ -585,15 +628,17 @@ import gettext from 'sources/gettext';
};
this.destroy = function() {
$editor.destroy();
$wrapper.remove();
};
this.focus = function() {
$input.trigger('focus');
$editor.focus();
};
this.loadValue = function(item) {
var data = defaultValue = item[args.column.field];
tmpdata = data;
if(args.column.column_type_internal === 'jsonb' && !Array.isArray(data)) {
data = JSONBigNumber.stringify(JSONBigNumber.parse(data), null, 4);
} else if (Array.isArray(data)) {
@@ -607,12 +652,39 @@ import gettext from 'sources/gettext';
});
data = '[' + temp.join() + ']';
}
$input.val(data);
$input.trigger('select');
/* if data is string then convert to json*/
if (typeof data === 'string')
data = JSON.parse(data);
require.ensure(['jsoneditor'], function(require) {
var JSONEditor = require('jsoneditor');
var jsonContainer = document.getElementById('pg-json-editor');
let options = {
modes: ['code', 'form', 'tree', 'preview'],
onEditable: function() {
return false;
}
};
if(jsonContainer) {
$editor = new JSONEditor(jsonContainer, options);
$editor.set(data);
}
}, function(error){
throw(error);
}, 'jsoneditorchunk');
};
this.serializeValue = function() {
return $input.val();
if($editor) {
let data = $editor.getText();
if (data === '') {
return null;
}
return data;
} else {
// The loader is not loaded yet ?
return tmpdata;
}
};
this.applyValue = function(item, state) {
@@ -620,12 +692,13 @@ import gettext from 'sources/gettext';
};
this.isValueChanged = function() {
return (!($input.val() == '' && defaultValue == null)) && ($input.val() != defaultValue);
let data = $editor.getText();
return (!(data == '' && defaultValue == null)) && (data != defaultValue);
};
this.validate = function() {
if (args.column.validator) {
var validationResults = args.column.validator($input.val());
var validationResults = args.column.validator($editor.getText());
if (!validationResults.valid) {
return validationResults;
}
@@ -39,13 +39,13 @@
}
}
function resizeAllColumns(maxWidth) {
function resizeAllColumns(maxWidth, max_width_changed=false) {
var elHeaders = $container.find('.slick-header-column');
var allColumns = grid.getColumns();
elHeaders.each(function(index, el) {
var columnDef = $(el).data('column');
// Check if width is set then no need to resize that column.
if (typeof(columnDef.width) !== 'undefined' && !isNaN(columnDef.width)) {
if (typeof(columnDef.width) !== 'undefined' && !isNaN(columnDef.width) && !max_width_changed) {
return;
}
@@ -0,0 +1,437 @@
.jsoneditor-menu a.jsoneditor-poweredBy {
visibility: hidden;
}
.jsoneditor {
border-color: $border-color;
}
/* Menu Bar*/
.jsoneditor-menu {
background: $color-gray-light;
border-bottom: 1px solid $border-color;
}
/* Menu Bar buttons*/
.jsoneditor-menu>button {
font-family: $font-family-icon !important;
font-size: $navbar-font-size !important;
line-height: $line-height-base !important;
background-image:none !important;
font-weight: 600 !important;
border-radius: $border-radius;
border-color:$color-gray;
opacity: 1;
background-color:$btn-primary-icon-bg;
color: $color-fg;
font-weight: normal !important;
}
/* Navigation Bar */
.jsoneditor-navigation-bar{
font-family: $font-family-primary !important;
background-color: $color-bg;
color: $color-fg;
border-bottom: 1px solid $color-gray;
}
/* Over rides icons */
.jsoneditor-menu>button.jsoneditor-expand-all::before{
content: "\f424";
font-weight: 600 !important;
}
.jsoneditor-menu>button.jsoneditor-collapse-all::before{
content: "\f422";
font-weight: 600 !important;
}
.jsoneditor-menu>button.jsoneditor-redo::before {
content: "\f01e";
font-weight: 600 !important;
}
.jsoneditor-menu>button.jsoneditor-undo::before {
content: "\f0e2";
font-weight: 600 !important;
}
.jsoneditor-menu>button.jsoneditor-format::before {
content: "\f03c";
font-weight: 600 !important;
}
.jsoneditor-menu>button.jsoneditor-compact::before {
content: "\f066";
font-weight: 600 !important;
}
.jsoneditor-menu>button.jsoneditor-sort::before {
content: "\f160";
font-weight: 600 !important;
}
.jsoneditor-menu>button.jsoneditor-repair::before {
content: "\f0ad";
font-weight: 600 !important;
}
.jsoneditor-menu>button.jsoneditor-transform::before {
content: "\f0b0";
font-weight: 600 !important;
}
/* Undo redo buttons */
.jsoneditor-menu>.jsoneditor-modes>button:disabled,
.jsoneditor-menu>button:disabled {
color:$color-fg !important;
opacity: $btn-disabled-opacity;
border: thin solid;
border-color:$color-gray;
border-radius: $border-radius;
background-color:$btn-primary-icon-bg !important;
}
/* Mode drop-down */
.jsoneditor-menu>.jsoneditor-modes>button,{
font-family: $font-family-primary !important;
font-size: $font-size-base !important;
line-height: $line-height-base !important;
background-image:none !important;
font-weight: 600 !important;
border-radius: $border-radius;
border-color:$color-gray;
opacity: 1;
background-color:$btn-primary-icon-bg;
color: $color-fg;
font-weight: normal !important;
}
/* Mode drop-down options */
.jsoneditor-contextmenu .jsoneditor-menu button,
.jsoneditor-contextmenu .jsoneditor-separator {
font-family: $font-family-primary !important;
font-size: $font-size-base !important;
background-color: $color-bg !important;
color: $color-fg !important;
border: none;
}
/* Drop-down hovered*/
.jsoneditor-contextmenu .jsoneditor-menu li button:hover {
background-color: $btn-primary-hover-bg !important;
border-color: $btn-primary-border-hover-bg !important;
color: $btn-primary-color !important;
}
/* Drop-down selected*/
.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected,
.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:focus,
.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:hover {
background-color: $btn-primary-hover-bg !important;
border-color: $btn-primary-border-hover-bg !important;
color: $btn-primary-color !important;
}
/* Search Box*/
.jsoneditor-frame,
.jsoneditor-search input{
color: $input-color;
background-color: $input-bg;
}
/* Set expand icon to none*/
div.jsoneditor-tree button.jsoneditor-button:focus {
background-color: $color-bg !important ;
outline: none;
}
/* Search Box results */
.jsoneditor-results {
color: $color-fg;
}
/* Ace editor Setting */
/* Ace editor numbers */
.ace-jsoneditor .ace_gutter {
background-color:$color-gray-light;
color:$color-fg;
}
/* Ace editor code background */
.ace-jsoneditor .ace_scroller {
background-color: $color-bg;
}
/* Ace editor hide indent guide */
.ace-jsoneditor .ace_indent-guide {
background: none;
}
/* Ace editor Key names */
.ace-jsoneditor .ace_variable {
color: $color-fg !important;
}
/* Ace editorfonts */
.ace_editor,
.ace-jsoneditor *{
font-family: $font-family-editor !important;
font-size: $font-size-base !important;
line-height: $line-height-base !important;
color: $color-fg;
}
.ace-jsoneditor.ace_editor {
font-family: $font-family-editor !important;
font-size: $font-size-base !important;
line-height: $line-height-base !important;
color: $color-fg;
background-color:$color-bg !important;
}
/* /* font setting all other mode */
/* form, tree, code, preview, schema-error */
div.jsoneditor-default,
div.jsoneditor-field,
div.jsoneditor-value,
div.jsoneditor textarea,
div.jsoneditor td,
div.jsoneditor-readonly,
.jsoneditor-popover,
div.jsoneditor-tree{
font-family: $font-family-primary !important;
font-size: $font-size-base !important;
line-height: $line-height-base !important;
color: $color-fg;
background-color:$color-bg;
}
div.jsoneditor td.jsoneditor-tree {
vertical-align: middle;
}
pre.jsoneditor-preview{
background-color: $color-gray-lighter !important;
}
/* Hilight selected values in tree/form modes */
div.jsoneditor-field.jsoneditor-highlight,
div.jsoneditor-field[contenteditable=true]:focus,
div.jsoneditor-field[contenteditable=true]:hover,
div.jsoneditor-value.jsoneditor-highlight,
div.jsoneditor-value[contenteditable=true]:focus,
div.jsoneditor-value[contenteditable=true]:hover
{
background-color: $sql-editor-selection-bg;
border: 1px solid $sql-editor-selection-bg;
border-radius: $border-radius;
}
/* Hi-light line in code */
.ace-jsoneditor .ace_marker-layer .ace_active-line {
background-color: $sql-editor-selection-bg;
border: 0;
border-radius: $border-radius;
}
.ace-jsoneditor .ace_gutter-active-line {
background-color:$color-gray-light;
}
.ace-jsoneditor .ace_marker-layer .ace_selected-word {
border: 1px solid $color-primary-light;
}
.ace-jsoneditor .ace_marker-layer .ace_selection {
background: $color-primary-light;
}
/* Nested DD*/
.jsoneditor-contextmenu .jsoneditor-icon {
background-image:none;
}
.jsoneditor-contextmenu .jsoneditor-text {
padding:4px 4px 4px 5px;
}
.jsoneditor-contextmenu .jsoneditor-menu{
border: 1px solid $border-color;
background: $color-bg !important;
color: $color-fg !important;
}
.jsoneditor-contextmenu .jsoneditor-menu button.jsoneditor-expand, {
height:0px;
background-color: $color-gray-light !important;
}
.jsoneditor-contextmenu .jsoneditor-menu li ul {
padding: 0;
border: 1px solid $border-color;
}
.jsoneditor-contextmenu .jsoneditor-separator {
height: 0;
border-top: 1px solid $border-color;
padding-top: 5px;
margin-top: 5px;
}
/* Status Bar */
.jsoneditor-statusbar {
background-color: $color-gray-light !important;
color: $color-primary-light-fg !important;
border-top: 1px solid $border-color;
}
/* Validation Bar */
.jsoneditor .jsoneditor-validation-errors {
background-color:$sql-history-error-bg !important;
color:$color-fg;
border: 1px solid $color-danger;
}
.jsoneditor .jsoneditor-text-errors {
width: 100%;
border-collapse: collapse;
border-top: 1px solid $color-gray;;
}
.jsoneditor .jsoneditor-text-errors tr,
.jsoneditor .jsoneditor-text-errors td,
.jsoneditor .jsoneditor-text-errors td pre,
.jsoneditor .jsoneditor-text-errors tr.parse-error {
background-color: $color-danger-lighter !important;
color:$color-fg;
}
/* Transform & sort */
/* Header */
.jsoneditor-modal .pico-modal-header {
font-family: $font-family-primary !important;
font-size: $font-size-base !important;
font-weight: bold;
background-color: $alert-header-bg !important;
color: $alert-header-fg;
}
/* Body */
.pico-content{
background-color: $color-gray-light !important;
}
/* Disable links */
.jsoneditor-modal a {
color: $color-fg !important;
}
/* Fields */
.jsoneditor-modal,
.jsoneditor-modal #query,
.jsoneditor-modal input,
.jsoneditor-modal input[type=text],
.jsoneditor-modal input[type=text]:focus,
.jsoneditor-modal option,
.jsoneditor-modal select,
.jsoneditor-modal table td,
.jsoneditor-modal table th,
.jsoneditor-modal textarea, {
color: $color-fg !important;
background-color: $color-bg !important;
font-size: $font-size-base !important;
font-family: $font-family-primary !important;
padding: 4px;
}
a:focus, input[type=checkbox]:focus, select:focus{
outline:none !important;
}
.jsoneditor-modal option{
background-color: $color-primary-light !important;
border: 1px solid $color-primary-light !important;
border-radius: $border-radius !important;
color:$color-fg !important;
}
/* Ascending descending buttons */
.jsoneditor-modal .jsoneditor-button-group.jsoneditor-button-group-value-asc input.jsoneditor-button-asc,
.jsoneditor-modal .jsoneditor-button-group.jsoneditor-button-group-value-desc input.jsoneditor-button-desc {
//color: $color-bg !important;
background-color: $color-primary !important;
//border-color: $color-primary !important;
border-color: $color-primary;
color: $btn-primary-color !important;
}
.jsoneditor-modal input[type=button]{
color: $color-fg;
background-color: $color-bg;
}
/* OK Button */
.jsoneditor-modal input[type=submit] {
color: $btn-primary-color !important;
border-color: $color-primary !important;
background-color: $color-primary !important;
text-transform: uppercase;
}
.jsoneditor-modal .jsoneditor-jmespath-label {
color: $color-fg;
font-weight: bold;
}
.selectr-option,
.selectr-options-container {
color: $color-fg;
background-color: $color-bg;
}
.selectr-option.active,
.selectr-option.selected{
color: $btn-primary-color !important;
background-color: $color-primary !important;
border-color: $color-primary;
}
.jsoneditor-modal .selectr-selected{
color: $color-fg;
background-color: $color-bg;
}
.jsoneditor-modal .selectr-selected .selectr-tag {
border: 1px solid $color-primary;
color: $btn-primary-color !important;
background-color: $color-primary !important;
border-color: $color-primary;
}
.jsoneditor-modal .selectr-selected .selectr-tag-remove::before{
color: $color-primary !important;
}
/* Update String. Key colors code mode */
.ace_variable {color: $color-editor-variable !important;}
.ace_string {color: $color-editor-string !important;}
.ace_constant.ace_numeric {color:$color-editor-number !important;}
/* Update String. Key colors tree/form mode*/
div.jsoneditor-value.jsoneditor-string {
color: $color-editor-string !important;
}
div.jsoneditor-value.jsoneditor-number {
color:$color-editor-number !important;
}
+1
View File
@@ -32,3 +32,4 @@ $theme-colors: (
@import 'bootstrap4-toggle.overrides';
@import 'pickr.overrides';
@import 'tippy.overrides';
@import 'jsoneditor.overrides';
@@ -114,6 +114,7 @@ define('pgadmin.datagrid', [
label: gettext('Query Tool'),
icon: 'pg-font-icon icon-query_tool',
data:{
applies: 'tools',
data_disabled: gettext('Please select a database from the browser tree to access Query Tool.'),
},
}];
@@ -210,7 +211,25 @@ define('pgadmin.datagrid', [
// This is a callback function to show query tool when user click on menu item.
show_query_tool: function(url, aciTreeIdentifier) {
const transId = commonUtils.getRandomInt(1, 9999999);
showQueryTool.showQueryTool(this, pgBrowser, alertify, url, aciTreeIdentifier, transId);
var t = pgBrowser.tree,
i = aciTreeIdentifier || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
//Open query tool with create script if copy_sql_to_query_tool is true else open blank query tool
var preference = pgBrowser.get_preference('sqleditor', 'copy_sql_to_query_tool');
if(preference.value && !d._type.includes('coll-') && (url === '' || url['applies'] === 'tools')){
var stype = d._type.toLowerCase();
var data = {
'script': stype,
data_disabled: gettext('The selected tree node does not support this option.'),
};
pgBrowser.Node.callbacks.show_script(data);
}else{
if(d._type.includes('coll-')){
url = '';
}
showQueryTool.showQueryTool(this, pgBrowser, alertify, url, aciTreeIdentifier, transId);
}
},
launch_grid: function(trans_id, panel_url, is_query_tool, panel_title, sURL=null, sql_filter=null) {
@@ -23,7 +23,7 @@ function isServerInformationAvailable(parentData) {
return parentData.server === undefined;
}
export function getPanelTitle(pgBrowser, selected_item=null, custom_title=null, parentData=null, conn_title=false) {
export function getPanelTitle(pgBrowser, selected_item=null, custom_title=null, parentData=null, conn_title=false, db_label=null) {
var preferences = pgBrowser.get_preferences_for_module('browser');
if(selected_item == null && parentData == null) {
selected_item = pgBrowser.treeMenu.selected();
@@ -36,7 +36,10 @@ export function getPanelTitle(pgBrowser, selected_item=null, custom_title=null,
}
}
const db_label = getDatabaseLabel(parentData);
if(!db_label) {
db_label = getDatabaseLabel(parentData);
}
var qt_title_placeholder = '';
if (!conn_title) {
if (custom_title) {
+16 -14
View File
@@ -333,20 +333,7 @@ export function initialize(gettext, url_for, $, _, pgAdmin, csrfToken, Browser)
// Listen key press event from terminal and emit socket event.
term.attachCustomKeyEventHandler(e => {
e.stopPropagation();
if(e.type=='keydown' && (e.metaKey || e.ctrlKey) &&(e.key == 'v' || e.key == 'V')) {
navigator.permissions.query({ name: 'clipboard-read' }).then(function(result) {
if(result.state === 'granted' || result.state === 'prompt') {
navigator.clipboard.readText().then( clipText => {
var selected_text = clipText;
if (selected_text.length > 0) {
socket.emit('socket_input', {'input': selected_text, 'key_name': e.code});
}
});
} else{
Alertify.alert(gettext('Clipboard read permission required'), gettext('To paste data on the PSQL terminal, Clipboard read permission required.'));
}
});
}else if(e.type=='keydown' && (e.metaKey || e.ctrlKey) && (e.key == 'c' || e.key == 'C')) {
if(e.type=='keydown' && (e.metaKey || e.ctrlKey) && (e.key == 'c' || e.key == 'C')) {
document.execCommand('copy');
}
@@ -357,6 +344,21 @@ export function initialize(gettext, url_for, $, _, pgAdmin, csrfToken, Browser)
return true;
});
term.textarea.addEventListener('paste', function() {
navigator.permissions.query({ name: 'clipboard-read' }).then(function(result) {
if(result.state === 'granted' || result.state === 'prompt') {
navigator.clipboard.readText().then( clipText => {
var selected_text = clipText;
if (selected_text.length > 0) {
socket.emit('socket_input', {'input': selected_text, 'key_name': 'paste'});
}
});
} else{
Alertify.alert(gettext('Clipboard read permission required'), gettext('To paste data on the PSQL terminal, Clipboard read permission required.'));
}
});
});
term.onKey(function (ev) {
socket.emit('socket_input', {'input': ev.key, 'key_name': ev.domEvent.code});
});
+6 -2
View File
@@ -98,8 +98,12 @@ class SearchObjectsHelper:
"""
if obj_type == 'all':
status, error = conn.execute_dict('select * from pg_subscription')
if 'permission denied' in error:
status, result = conn.execute_dict(
"SELECT COUNT(1) FROM information_schema.table_privileges "
"WHERE table_name = 'pg_subscription' "
"AND privilege_type = 'SELECT'")
if 'count' in result['rows'][0] and \
result['rows'][0]['count'] == '0':
skip_obj_type.append('subscription')
return skip_obj_type
@@ -892,6 +892,13 @@ define('tools.querytool', [
column_size[table_name] = {};
}
// Keep track of column_data_max_width
self.max_width_changed = false;
if (_.isUndefined(self.old_column_data_max_width) || self.old_column_data_max_width != self.preferences.column_data_max_width) {
self.old_column_data_max_width = self.preferences.column_data_max_width;
self.max_width_changed = true;
}
_.each(columns, function(c) {
c.display_name = _.escape(c.display_name);
@@ -931,12 +938,12 @@ define('tools.querytool', [
}
if (_.isUndefined(column_size[table_name][options.nonative_field])) {
/* If column_data_auto_resize is true then for the first time set
* the addWidth parameter to iconWidth and if it is false then
/* If column_data_auto_resize is 'by_data' then for the first time set
* the addWidth parameter to iconWidth and if it is 'by_name' then
* calculate width based on longer string among data type or
* column name.
*/
if (self.preferences.column_data_auto_resize) {
if (self.preferences.column_data_auto_resize === 'by_data') {
options['addWidth'] = iconWidth;
options['width'] = NaN;
} else {
@@ -1187,9 +1194,9 @@ define('tools.querytool', [
dataView.onRowsChanged.subscribe(function(e, args) {
grid.invalidateRows(args.rows);
grid.render();
// Resize all columns if column_data_auto_resize is true.
if (self.preferences.column_data_auto_resize) {
grid.resizeAllColumns && grid.resizeAllColumns(self.preferences.column_data_max_width);
// Resize all columns if column_data_auto_resize is 'by_data'.
if (self.preferences.column_data_auto_resize === 'by_data') {
grid.resizeAllColumns && grid.resizeAllColumns(self.preferences.column_data_max_width, self.max_width_changed);
}
});
@@ -1430,7 +1437,7 @@ define('tools.querytool', [
}
dataView.setItems(collection, self.client_primary_key);
/* Resize the columns once if data empty */
if (collection.length === 0 && self.preferences.column_data_auto_resize) {
if (collection.length === 0 && self.preferences.column_data_auto_resize === 'by_data') {
self.grid.resizeAllColumns && self.grid.resizeAllColumns();
}
},
@@ -2704,7 +2711,10 @@ define('tools.querytool', [
var server_data = pgWindow.default.pgAdmin.Browser.treeMenu.findNode(tree_data.slice(0,2));
var database_data = pgWindow.default.pgAdmin.Browser.treeMenu.findNode(tree_data.slice(0,4));
let conn_title = panelTitleFunc.getPanelTitle(pgWindow.default.pgAdmin.Browser, null, null, null, true);
var dbData = tree_data.slice(0,3);
dbData.push('database/' + url_params.did);
var dbName = pgWindow.default.pgAdmin.Browser.treeMenu.findNode(dbData).data.label;
let conn_title = panelTitleFunc.getPanelTitle(pgWindow.default.pgAdmin.Browser, null, null, null, true, dbName);
self.gridView.set_editor_title(_.unescape(conn_title));
let connection_data = {
'server_group': self.gridView.handler.url_params.sgid,
@@ -300,8 +300,22 @@ li.CodeMirror-hint-active {
& .pg-text-invalid {
background: $color-danger-lighter;
}
& #pg-json-editor {
min-width:525px;
min-height:280px;
height:295px;
width:550px;
border: $panel-border;
outline:0;
resize: both;
overflow:auto
}
}
.pg_buttons {
padding-top: 3px;
}
.sql-editor-message {
white-space:pre-wrap;
font-family: $font-family-editor;
@@ -128,6 +128,17 @@ def register_query_tool_preferences(self):
)
)
self.show_prompt_commit_transaction = self.preference.register(
'Options', 'copy_sql_to_query_tool',
gettext("Copy SQL from main window to query tool?"), 'boolean',
False,
category_label=PREF_LABEL_OPTIONS,
help_str=gettext(
'Specifies whether or not to copy SQL to query tool from '
'main window.'
)
)
self.sql_font_size = self.preference.register(
'Editor', 'plain_editor_mode',
gettext("Plain text mode?"), 'boolean', False,
@@ -270,14 +281,17 @@ def register_query_tool_preferences(self):
self.column_data_auto_resize = self.preference.register(
'Results_grid', 'column_data_auto_resize',
gettext("Resize by data?"), 'boolean', True,
gettext("Columns sized by"), 'radioModern', 'by_data',
options=[{'label': gettext('Column data'), 'value': 'by_data'},
{'label': gettext('Column name'), 'value': 'by_name'}],
category_label=PREF_LABEL_RESULTS_GRID,
help_str=gettext(
'If set to True then data columns will auto-size to the maximum '
'If set to \'Column data\' columns will auto-size to the maximum '
'width of the data in the column as loaded in the first batch. If '
'False, the column will be sized to the widest of the data type '
'or column name.'
)
'set to \'Column name\', the column will be sized to the widest '
'of the data type or column name.'
),
dependents=['column_data_max_width']
)
self.column_data_max_width = self.preference.register(
@@ -285,12 +299,9 @@ def register_query_tool_preferences(self):
gettext("Maximum column width"), 'integer', 0,
category_label=PREF_LABEL_RESULTS_GRID,
help_str=gettext(
'Specify the maximum width of the column when \'Resize by data?\' '
'is set to True. If it is set to 0 then columns will auto-size to '
'the maximum width of the data in the column. If '
'\'Resize by data?\' is set to False then this setting won\'t '
'take any effect.'
)
'Specify the maximum width of the column in pixels when '
'\'Columns sized by \' is set to \'Column data\'.'
),
)
self.sql_font_size = self.preference.register(
+15 -4
View File
@@ -129,6 +129,13 @@ def validate_user(data):
if 'auth_source' in data and data['auth_source'] != "":
new_data['auth_source'] = data['auth_source']
if 'locked' in data and type(data['locked']) == bool:
new_data['locked'] = data['locked']
if data['locked']:
new_data['login_attempts'] = config.MAX_LOGIN_ATTEMPTS
else:
new_data['login_attempts'] = 0
return new_data
@@ -207,7 +214,8 @@ def user(uid):
'email': u.email,
'active': u.active,
'role': u.roles[0].id,
'auth_source': u.auth_source
'auth_source': u.auth_source,
'locked': u.locked
}
else:
users = User.query.all()
@@ -219,7 +227,8 @@ def user(uid):
'email': u.email,
'active': u.active,
'role': u.roles[0].id,
'auth_source': u.auth_source
'auth_source': u.auth_source,
'locked': u.locked
})
res = users_data
@@ -316,7 +325,8 @@ def create_user(data):
'username': usr.username,
'email': usr.email,
'active': usr.active,
'role': usr.roles[0].id
'role': usr.roles[0].id,
'locked': usr.locked
}
@@ -599,7 +609,8 @@ def update(uid):
'email': usr.email,
'active': usr.active,
'role': usr.roles[0].id,
'auth_source': usr.auth_source
'auth_source': usr.auth_source,
'locked': usr.locked
}
return ajax_response(
@@ -436,6 +436,16 @@ define([
editable: function(m) {
return (m.get('auth_source') == DEFAULT_AUTH_SOURCE);
},
},{
id: 'locked',
label: gettext('Locked'),
type: 'switch',
cell: 'switch',
disabled: false,
sortable: false,
editable: function (m){
return (m.get('id') != userInfo['id']);
},
}],
validate: function() {
var errmsg = null,
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
+4 -1
View File
@@ -73,6 +73,7 @@ class _Preference(object):
self.fields = kwargs.get('fields', None)
self.allow_blanks = kwargs.get('allow_blanks', None)
self.disabled = kwargs.get('disabled', False)
self.dependents = kwargs.get('dependents', None)
# Look into the configuration table to find out the id of the specific
# preference.
@@ -254,6 +255,7 @@ class _Preference(object):
'value': self.get(),
'fields': self.fields,
'disabled': self.disabled,
'dependents': self.dependents
}
return res
@@ -427,6 +429,7 @@ class Preferences(object):
fields = kwargs.get('fields', None)
allow_blanks = kwargs.get('allow_blanks', None)
disabled = kwargs.get('disabled', False)
dependents = kwargs.get('dependents', None)
cat = self.__category(category, category_label)
if name in cat['preferences']:
@@ -444,7 +447,7 @@ class Preferences(object):
cat['id'], name, label, _type, default, help_str=help_str,
min_val=min_val, max_val=max_val, options=options,
select2=select2, fields=fields, allow_blanks=allow_blanks,
disabled=disabled
disabled=disabled, dependents=dependents
)
return res
+7
View File
@@ -68,6 +68,11 @@ class NavMenuLocators:
"//div[span[normalize-space(text())='Insert bracket pairs?']]" \
"//div[contains(@class,'toggle btn')]"
copy_sql_to_query_tool_switch_btn = \
"//div[span[normalize-space(text())=" \
"'Copy SQL from main window to query tool?']]" \
"//div[contains(@class,'toggle btn')]"
backup_filename_txt_box_name = "file"
restore_file_name_txt_box_name = "file"
@@ -183,6 +188,8 @@ class QueryToolLocators:
row_editor_text_area_css = ".pg-text-editor > textarea"
json_editor_text_area_css = "div.ace_layer.ace_text-layer"
text_editor_ok_btn_css = ".btn.btn-primary.long_text_editor"
btn_load_file_css = "#btn-load-file"
+1
View File
@@ -143,6 +143,7 @@ var webpackShimConfig = {
'dropzone': path.join(__dirname, './node_modules/dropzone/dist/dropzone'),
'bignumber': path.join(__dirname, './node_modules/bignumber.js/bignumber'),
'json-bignumber': path.join(__dirname, './node_modules/json-bignumber/dist/JSONBigNumber.min'),
'jsoneditor': path.join(__dirname, './node_modules/jsoneditor/dist/jsoneditor.min'),
'snap.svg': path.join(__dirname, './node_modules/snapsvg-cjs/dist/snap.svg-cjs'),
'color-picker': path.join(__dirname, './node_modules/@simonwep/pickr/dist/pickr.es5.min'),
'mousetrap': path.join(__dirname, './node_modules/mousetrap'),
+63 -1
View File
@@ -1214,6 +1214,11 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
"@sphinxxxx/color-conversion@^2.2.2":
version "2.2.2"
resolved "https://registry.yarnpkg.com/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz#03ecc29279e3c0c832f6185a5bfa3497858ac8ca"
integrity sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==
"@tippyjs/react@^4.2.0":
version "4.2.5"
resolved "https://registry.yarnpkg.com/@tippyjs/react/-/react-4.2.5.tgz#9b5837db93a1cac953962404df906aef1a18e80d"
@@ -1500,6 +1505,11 @@ accepts@~1.3.4:
mime-types "~2.1.24"
negotiator "0.6.2"
ace-builds@^1.4.12:
version "1.4.12"
resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.12.tgz#888efa386e36f4345f40b5233fcc4fe4c588fae7"
integrity sha512-G+chJctFPiiLGvs3+/Mly3apXTcfgE45dT5yp12BcWZ1kUs+gm0qd3/fv4gsz6fVag4mM0moHVpjHDIgph6Psg==
"acitree@git+https://github.com/imsurinder90/jquery-aciTree.git#rc.7":
version "4.5.0-rc.7"
resolved "git+https://github.com/imsurinder90/jquery-aciTree.git#24dcd7536a008abe25da6adb7bfde8eeb53892f1"
@@ -1568,7 +1578,7 @@ ajv@^5.0.0:
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -5443,6 +5453,11 @@ jasmine-enzyme@^7.1.2:
dependencies:
enzyme-matchers "^7.1.2"
javascript-natural-sort@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59"
integrity sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=
jest-worker@^26.3.0, jest-worker@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
@@ -5452,6 +5467,11 @@ jest-worker@^26.3.0, jest-worker@^26.6.2:
merge-stream "^2.0.0"
supports-color "^7.0.0"
jmespath@^0.15.0:
version "0.15.0"
resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217"
integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=
jquery-contextmenu@^2.6.4, jquery-contextmenu@^2.9.2:
version "2.9.2"
resolved "https://registry.yarnpkg.com/jquery-contextmenu/-/jquery-contextmenu-2.9.2.tgz#f9dc362e45871dda2e50fa45de2243e917446ced"
@@ -5559,6 +5579,11 @@ json-schema@0.2.3:
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
json-source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/json-source-map/-/json-source-map-0.6.1.tgz#e0b1f6f4ce13a9ad57e2ae165a24d06e62c79a0f"
integrity sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@@ -5583,6 +5608,21 @@ json5@^2.1.2:
dependencies:
minimist "^1.2.5"
jsoneditor@^9.5.1:
version "9.5.3"
resolved "https://registry.yarnpkg.com/jsoneditor/-/jsoneditor-9.5.3.tgz#a44cce2556a21e13db360865ed4d4224908df3da"
integrity sha512-KW893jRnh/s41n7Is4aNFg5Gh2h4HhE9W8L2rP1Okue3aW5Aov9SRqXs/5rmAABmlBGx1yzyPMlj4Oqw5nXQcQ==
dependencies:
ace-builds "^1.4.12"
ajv "^6.12.6"
javascript-natural-sort "^0.7.1"
jmespath "^0.15.0"
json-source-map "^0.6.1"
jsonrepair "^2.2.1"
mobius1-selectr "^2.4.13"
picomodal "^3.0.0"
vanilla-picker "^2.11.2"
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -5595,6 +5635,11 @@ jsonparse@^1.2.0:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=
jsonrepair@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/jsonrepair/-/jsonrepair-2.2.1.tgz#7c6257c36550a310150c41ab7d5d4cab71828456"
integrity sha512-o9Je8TceILo872uQC9fIBJm957j1Io7z8Ca1iWIqY6S5S65HGE9XN7XEEw7+tUviB9Vq4sygV89MVTxl+rhZyg==
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -6212,6 +6257,11 @@ ml-matrix@^6.5.0:
dependencies:
ml-array-rescale "^1.3.5"
mobius1-selectr@^2.4.13:
version "2.4.13"
resolved "https://registry.yarnpkg.com/mobius1-selectr/-/mobius1-selectr-2.4.13.tgz#0019dfd9f984840d6e40f70683ab3ec78ce3b5df"
integrity sha512-Mk9qDrvU44UUL0EBhbAA1phfQZ7aMZPjwtL7wkpiBzGh8dETGqfsh50mWoX9EkjDlkONlErWXArHCKfoxVg0Bw==
module-deps@^6.2.3:
version "6.2.3"
resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.3.tgz#15490bc02af4b56cf62299c7c17cba32d71a96ee"
@@ -6849,6 +6899,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
picomodal@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/picomodal/-/picomodal-3.0.0.tgz#facd30f4fbf34a809c1e04ea525f004f399c0b82"
integrity sha1-+s0w9PvzSoCcHgTqUl8ATzmcC4I=
pify@^2.0.0, pify@^2.2.0, pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -8972,6 +9027,13 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
vanilla-picker@^2.11.2:
version "2.11.2"
resolved "https://registry.yarnpkg.com/vanilla-picker/-/vanilla-picker-2.11.2.tgz#eaa24efa68c27e7ee9e0776df55d6913b855f133"
integrity sha512-2cP7LlUnxHxwOf06ReUVtd2RFJMnJGaxN2s0p8wzBH3In5b00Le7fFZ9VrIoBE0svZkSq/BC/Pwq/k/9n+AA2g==
dependencies:
"@sphinxxxx/color-conversion" "^2.2.2"
varstream@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/varstream/-/varstream-0.3.2.tgz#18ac6494765f3ff1a35ad9a4be053bec188a5de1"