From ca8b5c68fd8ded513dab3e5c99135acfced8ca2d Mon Sep 17 00:00:00 2001 From: Khushboo Vashi Date: Sat, 10 Sep 2022 14:22:49 +0530 Subject: [PATCH] Remove Backgrid and Backform. Fixes #6134 --- docs/en_US/code_overview.rst | 2 +- docs/en_US/release_notes_6_14.rst | 1 + web/package.json | 3 - .../static/js/foreign_server.js | 1 - .../user_mappings/static/js/user_mapping.js | 32 +- .../static/js/foreign_data_wrapper.js | 2 +- .../databases/languages/static/js/language.js | 2 +- .../publications/static/js/publication.js | 2 +- .../schemas/functions/static/js/function.js | 2 +- .../schemas/functions/static/js/procedure.js | 2 +- .../functions/static/js/trigger_function.js | 2 +- .../packages/edbfuncs/static/js/edbfunc.js | 2 +- .../packages/edbfuncs/static/js/edbproc.js | 1 - .../packages/edbvars/static/js/edbvar.js | 2 +- .../databases/schemas/static/js/schema.js | 323 +- .../index_constraint/static/js/primary_key.js | 445 +-- .../schemas/views/static/js/mview.js | 1 - .../databases/schemas/views/static/js/view.js | 2 +- .../servers/databases/static/js/database.js | 1 - .../pgagent/steps/static/js/pga_jobstep.js | 36 +- .../servers/static/js/privilege.js | 765 ---- .../server_groups/servers/static/js/server.js | 1 - .../servers/static/js/variable.js | 490 --- .../tablespaces/static/js/tablespace.js | 3 +- web/pgadmin/browser/static/js/collection.js | 3 +- web/pgadmin/browser/static/js/node.js | 674 +--- web/pgadmin/browser/static/js/node.ui.js | 564 --- .../dashboard/static/css/dashboard.css | 2 +- web/pgadmin/settings/static/js/settings.js | 2 +- web/pgadmin/static/css/style.css | 4 - web/pgadmin/static/js/backform.pgadmin.js | 3558 ----------------- web/pgadmin/static/js/backgrid.pgadmin.js | 2318 ----------- web/pgadmin/static/js/tree/tree_init.tsx | 4 +- .../static/scss/_backform.overrides.scss | 65 - .../static/scss/_backgrid.overrides.scss | 397 -- web/pgadmin/static/scss/pgadmin.scss | 2 - .../static/vendor/backform/backform.js | 658 --- .../vendor/backgrid/backgrid-select-all.css | 12 - .../vendor/backgrid/backgrid-select-all.js | 303 -- .../static/vendor/backgrid/backgrid.css | 236 -- .../static/vendor/backgrid/backgrid.js | 3011 -------------- web/pgadmin/tools/debugger/__init__.py | 7 +- .../xss_checks_panels_and_query_tool_test.py | 8 +- .../xss_checks_pgadmin_debugger_test.py | 2 +- .../keyboardshortcut_spec.js | 434 -- .../backform_controls/keycode_spec.js | 199 - web/webpack.config.js | 1 - web/webpack.shim.js | 37 +- web/webpack.test.config.js | 5 - web/yarn.lock | 49 +- 50 files changed, 77 insertions(+), 14601 deletions(-) delete mode 100644 web/pgadmin/browser/server_groups/servers/static/js/privilege.js delete mode 100644 web/pgadmin/browser/server_groups/servers/static/js/variable.js delete mode 100644 web/pgadmin/browser/static/js/node.ui.js delete mode 100644 web/pgadmin/static/js/backform.pgadmin.js delete mode 100644 web/pgadmin/static/js/backgrid.pgadmin.js delete mode 100644 web/pgadmin/static/scss/_backform.overrides.scss delete mode 100644 web/pgadmin/static/scss/_backgrid.overrides.scss delete mode 100644 web/pgadmin/static/vendor/backform/backform.js delete mode 100644 web/pgadmin/static/vendor/backgrid/backgrid-select-all.css delete mode 100644 web/pgadmin/static/vendor/backgrid/backgrid-select-all.js delete mode 100644 web/pgadmin/static/vendor/backgrid/backgrid.css delete mode 100644 web/pgadmin/static/vendor/backgrid/backgrid.js delete mode 100644 web/regression/javascript/backform_controls/keyboardshortcut_spec.js delete mode 100644 web/regression/javascript/backform_controls/keycode_spec.js diff --git a/docs/en_US/code_overview.rst b/docs/en_US/code_overview.rst index 946a24c6c..6b00d41a7 100644 --- a/docs/en_US/code_overview.rst +++ b/docs/en_US/code_overview.rst @@ -208,7 +208,7 @@ Front End pgAdmin uses javascript extensively for the front-end implementation. It uses require.js to allow the lazy loading (or, say load only when required), bootstrap for UI look and feel, Backbone for data manipulation of a node, -Backform for generating properties/create dialog for selected node. We have +React for generating properties/create dialog for selected node. We have divided each module in small chunks as much as possible. Not all javascript modules are required to be loaded (i.e. loading a javascript module for database will make sense only when a server node is loaded completely.) Please diff --git a/docs/en_US/release_notes_6_14.rst b/docs/en_US/release_notes_6_14.rst index b03564272..96219282c 100644 --- a/docs/en_US/release_notes_6_14.rst +++ b/docs/en_US/release_notes_6_14.rst @@ -20,6 +20,7 @@ Housekeeping ************ | `Issue #6133 `_ - Port schema diff to React. + | `Issue #6134 `_ - Remove Backgrid and Backform. | `Issue #7343 `_ - Port the remaining components of the ERD Tool to React. | `Issue #7619 `_ - Remove Alertify from pgAdmin completely. | `Issue #7622 `_ - Port search object dialog to React. diff --git a/web/package.json b/web/package.json index 9eb2850b0..b7604d2cf 100644 --- a/web/package.json +++ b/web/package.json @@ -97,9 +97,6 @@ "axios": "^0.21.1", "babelify": "~10.0.0", "backbone": "1.4.0", - "backform": "^0.2.0", - "backgrid-filter": "^0.3.7", - "backgrid-select-all": "^0.3.5", "bignumber.js": "^9.0.1", "bootstrap": "^4.3.1", "bootstrap-datepicker": "^1.8.0", diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/js/foreign_server.js b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/js/foreign_server.js index 026e2439f..d15ddae86 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/js/foreign_server.js +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/js/foreign_server.js @@ -14,7 +14,6 @@ import ForeignServerSchema from './foreign_server.ui'; define('pgadmin.node.foreign_server', [ 'sources/gettext', 'sources/url_for', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.browser.collection', - 'pgadmin.browser.server.privilege', ], function(gettext, url_for, pgAdmin, pgBrowser) { // Extend the browser's collection class for foreign server collection diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mappings/static/js/user_mapping.js b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mappings/static/js/user_mapping.js index 0c220a2bc..e20f36460 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mappings/static/js/user_mapping.js +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mappings/static/js/user_mapping.js @@ -14,8 +14,8 @@ import _ from 'lodash'; define('pgadmin.node.user_mapping', [ 'sources/gettext', 'sources/url_for', 'sources/pgadmin', 'pgadmin.browser', - 'pgadmin.backform', 'pgadmin.browser.collection', -], function(gettext, url_for, pgAdmin, pgBrowser, Backform) { + 'pgadmin.browser.collection', +], function(gettext, url_for, pgAdmin, pgBrowser) { // Extend the browser's collection class for user mapping collection if (!pgBrowser.Nodes['coll-user_mapping']) { @@ -105,34 +105,6 @@ define('pgadmin.node.user_mapping', [ } pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); }, - - // Defining schema for the user mapping node - schema: [ - { - id: 'name', label: gettext('User'), type: 'text', - control: Backform.NodeListByNameControl, node: 'role', - mode: ['edit', 'create', 'properties'], select2: { allowClear: false }, - disabled: function(m) { return !m.isNew(); }, - transform: function() { - let self = this, - node = self.field.get('schema_node'); - let res = - Backform.NodeListByNameControl.prototype.defaults.transform.apply( - this, arguments - ); - res.unshift({ - label: 'CURRENT_USER', value: 'CURRENT_USER', - image: 'icon-' + node.type, - },{ - label: 'PUBLIC', value: 'PUBLIC', image: 'icon-' + node.type, - }); - return res; - }, - }, { - id: 'oid', label: gettext('OID'), cell: 'string', - type: 'text', mode: ['properties'], - } - ], }), }); diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/js/foreign_data_wrapper.js b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/js/foreign_data_wrapper.js index 4dcfc72af..00cbe51ad 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/js/foreign_data_wrapper.js +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/js/foreign_data_wrapper.js @@ -13,7 +13,7 @@ import ForeignDataWrapperSchema from './foreign_data_wrapper.ui'; define('pgadmin.node.foreign_data_wrapper', [ 'sources/gettext', 'sources/url_for', 'pgadmin.browser', - 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', + 'pgadmin.browser.collection', ], function(gettext, url_for, pgBrowser) { // Extend the browser's collection class for foreign data wrapper collection diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/static/js/language.js b/web/pgadmin/browser/server_groups/servers/databases/languages/static/js/language.js index 2ab91efb7..9378d6826 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/languages/static/js/language.js +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/static/js/language.js @@ -13,7 +13,7 @@ import { getNodePrivilegeRoleSchema } from '../../../../static/js/privilege.ui'; define('pgadmin.node.language', [ 'sources/gettext', 'sources/url_for', 'pgadmin.browser', - 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', + 'pgadmin.browser.collection', ], function(gettext, url_for, pgBrowser) { // Extend the browser's collection class for languages collection diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.js b/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.js index b77ad85e2..e3b7dd918 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.js +++ b/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.js @@ -12,7 +12,7 @@ import PublicationSchema from './publication.ui'; define('pgadmin.node.publication', [ 'sources/gettext', 'sources/url_for', 'pgadmin.browser', - 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', + 'pgadmin.browser.collection', ], function(gettext, url_for, pgBrowser) { // Extend the browser's collection class for publications collection diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js index f730bd154..a216222e6 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js @@ -16,7 +16,7 @@ import { getNodeVariableSchema } from '../../../../../static/js/variable.ui'; define('pgadmin.node.function', [ 'sources/gettext', 'sources/url_for', 'pgadmin.browser', 'pgadmin.node.schema.dir/child', 'pgadmin.node.schema.dir/schema_child_tree_node', - 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', + 'pgadmin.browser.collection', ], function( gettext, url_for, pgBrowser, schemaChild, schemaChildTreeNode ) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js index 2b779aaa2..b09d680eb 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js @@ -17,7 +17,7 @@ define('pgadmin.node.procedure', [ 'sources/gettext', 'sources/url_for', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.node.schema.dir/child', 'pgadmin.node.schema.dir/schema_child_tree_node', - 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', + 'pgadmin.browser.collection', ], function(gettext, url_for, pgAdmin, pgBrowser, schemaChild, schemaChildTreeNode) { if (!pgBrowser.Nodes['coll-procedure']) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js index 313d4c87d..afc1a3f33 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js @@ -16,7 +16,7 @@ import _ from 'lodash'; define('pgadmin.node.trigger_function', [ 'sources/gettext', 'sources/url_for', 'pgadmin.browser', 'pgadmin.node.schema.dir/child', 'pgadmin.node.schema.dir/schema_child_tree_node', - 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', + 'pgadmin.browser.collection', ], function( gettext, url_for, pgBrowser, schemaChild, schemaChildTreeNode ) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbfunc.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbfunc.js index 5bf697fe3..c9271ec7d 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbfunc.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbfunc.js @@ -12,7 +12,7 @@ import EDBFuncSchema from './edbfunc.ui'; /* Create and Register Function Collection and Node. */ define('pgadmin.node.edbfunc', [ 'sources/gettext', 'sources/url_for', 'pgadmin.browser', - 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', + 'pgadmin.browser.collection', ], function(gettext, url_for, pgBrowser) { if (!pgBrowser.Nodes['coll-edbfunc']) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbproc.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbproc.js index bf3c570a6..8ff5844a1 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbproc.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/js/edbproc.js @@ -15,7 +15,6 @@ define('pgadmin.node.edbproc', [ 'sources/gettext', 'sources/url_for', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.node.edbfunc', 'pgadmin.browser.collection', - 'pgadmin.browser.server.privilege', ], function( gettext, url_for, pgAdmin, pgBrowser, EdbFunction ) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/js/edbvar.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/js/edbvar.js index 490ad4a9c..ccfe4dba5 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/js/edbvar.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/js/edbvar.js @@ -12,7 +12,7 @@ import EDBVarSchema from './edbvar.ui'; /* Create and Register Function Collection and Node. */ define('pgadmin.node.edbvar', [ 'sources/gettext', 'sources/url_for', 'pgadmin.browser', - 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', + 'pgadmin.browser.collection', ], function(gettext, url_for, pgBrowser) { if (!pgBrowser.Nodes['coll-edbvar']) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js index 0b4b588f8..5f8cfc4f4 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js @@ -10,297 +10,11 @@ import PGSchema from './schema.ui'; import { getNodePrivilegeRoleSchema } from '../../../../static/js/privilege.ui'; import { getNodeListByName } from '../../../../../../static/js/node_ajax'; -import _ from 'lodash'; define('pgadmin.node.schema', [ 'sources/gettext', 'sources/url_for', 'jquery', - 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid', - 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', -], function(gettext, url_for, $, pgBrowser, Backform, Backgrid) { - - // VacuumSettings Collection to display all settings parameters as Grid - Backform.VacuumCollectionControl = - Backform.Control.extend({ - - grid_columns:undefined, - - initialize: function() { - Backform.Control.prototype.initialize.apply(this, arguments); - let self = this, - m = this.model, - url = self.field.get('url'); - - if (url && m.isNew()) { - let node = self.field.get('node'), - node_data = self.field.get('node_data'), - node_info = self.field.get('node_info'), - full_url = node.generate_url.apply( - node, [ - null, url, node_data, false, node_info, - ]), - data; - m.trigger('pgadmin-view:fetching', m, self.field); - - // fetch default values for autovacuum fields - $.ajax({ - async: false, - url: full_url, - }) - .done(function (res) { - data = res; - }) - .fail(function() { - m.trigger('pgadmin-view:fetch:error', m, self.field); - }); - m.trigger('pgadmin-view:fetched', m, self.field); - - // Add fetched models into collection - if (data && _.isArray(data)) { - m.get(self.field.get('name')).reset(data, {silent: true}); - } - } - }, - events : { - 'keydown': 'keyDownHandler', - }, - - keyDownHandler : function(event){ - // move the focus to editable input - let lastButton = $(this.$el).find('button:last'); - let firstEditableCell = $(this.$el).find('td.editable:first'); - if (event.keyCode== 9 && !event.shiftKey){ - if ($(firstEditableCell).is(':visible') - && ($(event.target)).is($(lastButton))){ - $(firstEditableCell).trigger('click'); - event.preventDefault(); - event.stopPropagation(); - } - } - }, - - render: function() { - let self = this, - attributes = self.field.attributes; - - // remove grid - if(self.grid) { - self.grid.remove(); - delete self.grid; - self.grid = undefined; - } - - self.$el.empty(); - - let gridHeader = _.template([ - '
', - '<% if (label && label != "") %> {', - ' <%-label%>', - '}', - '
'].join('\n')), - gridBody = $('
').append( - gridHeader(attributes) - ); - - // Initialize a new Grid instance - let grid = self.grid = new Backgrid.Grid({ - columns: self.grid_columns, - collection: self.model.get(self.field.get('name')), - className: 'backgrid table table-bordered table-noouter-border table-hover', - }); - - // render grid - self.$el.addClass('mb-0'); - self.$el.append($(gridBody).append(grid.render().$el)); - - return self; - }, - }); - - // We will use this function in VacuumSettings Control - // to convert data type on the fly - Backform.cellFunction = function(model) { - let vartype = model.get('column_type'); - - switch(vartype) { - case 'integer': - return Backgrid.IntegerCell; - case 'number': - return Backgrid.NumberCell.extend({ - decimals: this.get('decimals') || Backgrid.NumberFormatter.prototype.defaults.decimals, - }); - case 'string': - return Backgrid.StringCell; - default: - return Backgrid.Cell; - } - }; - - // Define Security Model with fields and validation for VacuumSettings Control - Backform.VacuumTableModel = pgBrowser.Node.Model.extend({ - defaults: { - name: undefined, - setting: undefined, - label:undefined, - value: undefined, - column_type: undefined, - }, - - toJSON: function(){ - let d = pgBrowser.Node.Model.prototype.toJSON.apply(this); - delete d.label; - delete d.setting; - delete d.column_type; - return d; - }, - }); - - /* As Backform.VacuumSettingsSchema is commonly used in table & partition, so keeping it as it is for now. - this and other supporting model can be removed after their migration to react. */ - // Extend the browser's collection class for VacuumSettingsModel - Backform.VacuumSettingsSchema = [{ - id: 'spacer_ctrl', group: gettext('Table'), mode: ['edit', 'create'], type: 'spacer', - },{ - id: 'autovacuum_custom', label: gettext('Custom auto-vacuum?'), - group: gettext('Table'), mode: ['edit', 'create'], - type: 'switch', controlLabelClassName: 'control-label pg-el-sm-4 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-8 pg-el-12', - disabled: function(m) { - // If table is partitioned table then disabled it. - if (m.top && m.top.get('is_partitioned')) { - // We also need to unset rest of all - setTimeout(function() { - m.set('autovacuum_custom', false); - }, 10); - - return true; - } - - return m.top.inSchema.apply(this, [m]); - }, - },{ - id: 'autovacuum_enabled', label: gettext('Autovacuum Enabled?'), - group: gettext('Table'), mode: ['edit', 'create'], - type: 'radioModern', controlLabelClassName: 'control-label pg-el-sm-4 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-8 pg-el-12', - options: [ - {'label': gettext('Not set'), 'value': 'x'}, - {'label': gettext('Yes'), 'value': 't'}, - {'label': gettext('No'), 'value': 'f'}, - ], - deps: ['autovacuum_custom'], - disabled: function(m) { - if(!m.top.inSchema.apply(this, [m]) && m.get('autovacuum_custom')) { - return false; - } - - // We also need to unset rest of all - setTimeout(function() { - m.set('autovacuum_enabled', 'x'); - }, 10); - return true; - }, - },{ - id: 'vacuum_table', label: '', - model: Backform.VacuumTableModel, editable: false, type: 'collection', - canEdit: true, group: gettext('Table'), - mode: ['edit', 'create'], url: 'get_table_vacuum', - control: Backform.VacuumCollectionControl.extend({ - grid_columns :[ - { - name: 'label', label: gettext('Label'), - headerCell: Backgrid.Extension.CustomHeaderCell, - cell: 'string', editable: false, cellHeaderClasses:'width_percent_40', - }, - { - name: 'value', label: gettext('Value'), - cellHeaderClasses:'width_percent_30', - decimals: 5, - cellFunction: Backform.cellFunction, editable: function(m) { - return m.handler.get('autovacuum_custom'); - }, headerCell: Backgrid.Extension.CustomHeaderCell, - }, - { - name: 'setting', label: gettext('Default'), - cellHeaderClasses:'width_percent_30', - headerCell: Backgrid.Extension.CustomHeaderCell, - cellFunction: Backform.cellFunction, editable: false, - }, - ], - }), - deps: ['autovacuum_custom'], - },{ - id: 'spacer_ctrl', group: gettext('TOAST table'), mode: ['edit', 'create'], type: 'spacer', - },{ - id: 'toast_autovacuum', label: gettext('Custom auto-vacuum?'), - group: gettext('TOAST table'), mode: ['edit', 'create'], - type: 'switch', controlLabelClassName: 'control-label pg-el-sm-4 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-8 pg-el-12', - disabled: function(m) { - // We need to check additional condition to toggle enable/disable - // for table auto-vacuum - return !(!m.top.inSchema.apply(this, [m]) && - (m.isNew() || (m.get('toast_autovacuum_enabled') === true || m.top.get('hastoasttable') === true))); - }, - },{ - id: 'toast_autovacuum_enabled', label: gettext('Autovacuum Enabled?'), - group: gettext('TOAST table'), mode: ['edit', 'create'], - type: 'radioModern', controlLabelClassName: 'control-label pg-el-sm-4 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-8 pg-el-12', - options: [ - {'label': gettext('Not set'), 'value': 'x'}, - {'label': gettext('Yes'), 'value': 't'}, - {'label': gettext('No'), 'value': 'f'}, - ], - deps:['toast_autovacuum'], - disabled: function(m) { - // If in schema & in create mode then enable it - if(!m.top.inSchema.apply(this, [m]) && - m.get('toast_autovacuum') === true) { - - return false; - } - - if (m.isNew() || m.get('hastoasttable')) { - // we also need to unset rest of all - setTimeout(function() { - m.set('toast_autovacuum_enabled', 'x'); - }, 10); - } - return true; - }, - },{ - id: 'vacuum_toast', label: '', - model: Backform.VacuumTableModel, type: 'collection', editable: function(m) { - return m.isNew(); - }, - canEdit: true, group: gettext('TOAST table'), - mode: ['properties', 'edit', 'create'], url: 'get_toast_table_vacuum', - control: Backform.VacuumCollectionControl.extend({ - grid_columns :[ - { - name: 'label', label: gettext('Label'), - headerCell: Backgrid.Extension.CustomHeaderCell, - cell: 'string', editable: false, cellHeaderClasses:'width_percent_40', - }, - { - name: 'value', label: gettext('Value'), - cellHeaderClasses:'width_percent_30', - headerCell: Backgrid.Extension.CustomHeaderCell, - decimals: 5, - cellFunction: Backform.cellFunction, editable: function(m) { - return m.handler.get('toast_autovacuum'); - }, - }, - { - name: 'setting', label: gettext('Default'), - cellHeaderClasses:'width_percent_30', - headerCell: Backgrid.Extension.CustomHeaderCell, - cellFunction: Backform.cellFunction, editable: false, - }, - ], - }), - deps: ['toast_autovacuum'], - }]; + 'pgadmin.browser', 'pgadmin.browser.collection', +], function(gettext, url_for, $, pgBrowser) { // Extend the browser's collection class for schema collection if (!pgBrowser.Nodes['coll-schema']) { @@ -376,38 +90,5 @@ define('pgadmin.node.schema', [ }; } - /* As TableChildSwitchCell is commonly used in index, column & TableDialog, so keeping it as it is for now. - this and other supporting model can be removed after their migration to react. */ - // Switch Cell with Deps (specifically for table children) - Backgrid.Extension.TableChildSwitchCell = Backgrid.Extension.SwitchCell.extend({ - initialize: function() { - Backgrid.Extension.SwitchCell.prototype.initialize.apply(this, arguments); - Backgrid.Extension.DependentCell.prototype.initialize.apply(this, arguments); - }, - dependentChanged: function () { - let model = this.model, - column = this.column, - editable = this.column.get('editable'), - input = this.$el.find('input[type=checkbox]').first(), - self_name = column.get('name'), - is_editable; - - is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable; - if (is_editable) { - this.$el.addClass('editable'); - input.bootstrapToggle('disabled',false); - } else { - this.$el.removeClass('editable'); - input.bootstrapToggle('disabled',true); - // Set self value into model to false - setTimeout(function() { model.set(self_name, false); }, 10); - } - - this.delegateEvents(); - return this; - }, - remove: Backgrid.Extension.DependentCell.prototype.remove, - }); - return pgBrowser.Nodes['schema']; }); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js index 006e70b9a..de4491be1 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js @@ -13,9 +13,9 @@ import _ from 'lodash'; define('pgadmin.node.primary_key', [ 'sources/gettext', 'sources/url_for', - 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid', + 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.browser.collection', -], function(gettext, url_for, pgAdmin, pgBrowser, Backform, Backgrid) { +], function(gettext, url_for, pgAdmin, pgBrowser) { // Extend the browser's node class for index constraint node if (!pgBrowser.Nodes['primary_key']) { @@ -166,447 +166,6 @@ define('pgadmin.node.primary_key', [ // We can't update columns of existing index constraint. return !m.isNew(); }, - // Define the schema for the index constraint node - schema: [{ - id: 'name', label: gettext('Name'), type: 'text', - mode: ['properties', 'create', 'edit'], editable:true, - cellHeaderClasses:'width_percent_40', - },{ - id: 'oid', label: gettext('OID'), cell: 'string', - type: 'text' , mode: ['properties'], editable: false, - cellHeaderClasses:'width_percent_20', - },{ - id: 'is_sys_obj', label: gettext('System primary key?'), - cell:'boolean', type: 'switch', mode: ['properties'], - },{ - id: 'comment', label: gettext('Comment'), cell: 'string', - type: 'multiline', mode: ['properties', 'create', 'edit'], - deps:['name'], disabled:function(m) { - let name = m.get('name'); - if (!(name && name != '')) { - setTimeout(function(){ - if(m.get('comment') && m.get('comment') !== '') { - m.set('comment', null); - } - },10); - return true; - } else { - return false; - } - }, - },{ - id: 'columns', label: gettext('Columns'), - type: 'collection', group: gettext('Definition'), - editable: false, - cell: Backgrid.StringCell.extend({ - initialize: function() { - Backgrid.StringCell.prototype.initialize.apply(this, arguments); - - let self = this, - collection = this.model.get('columns'); - - // Do not listen for any event(s) for existing constraint. - if (_.isUndefined(self.model.get('oid'))) { - let tableCols = self.model.top.get('columns'); - self.listenTo(tableCols, 'remove' , self.removeColumn); - self.listenTo(tableCols, 'change:name', self.resetColOptions); - } - - collection.on('pgadmin:multicolumn:updated', function() { - self.render.apply(self); - }); - self.listenTo(collection, 'add', self.render); - self.listenTo(collection, 'remove', self.render); - }, - removeColumn: function(m) { - let self = this, - removedCols = self.model.get('columns').where( - {column: m.get('name')} - ); - - self.model.get('columns').remove(removedCols); - setTimeout(function () { - self.render(); - }, 10); - - let key = 'primary_key'; - setTimeout(function () { - let constraints = self.model.top.get(key), - removed = []; - constraints.each(function(constraint) { - if (constraint.get('columns').length == 0) { - removed.push(constraint); - } - }); - constraints.remove(removed); - },100); - - }, - resetColOptions : function(m) { - let self = this, - updatedCols = self.model.get('columns').where( - {column: m.previous('name')} - ); - if (updatedCols.length > 0) { - /* - * Table column name has changed so update - * column name in primary key as well. - */ - updatedCols[0].set( - {'column': m.get('name')}, - {silent: true}); - } - - setTimeout(function () { - self.render(); - }, 10); - }, - formatter: { - fromRaw: function (rawValue) { - return rawValue.pluck('column').toString(); - }, - toRaw: function (val) { return val; }, - }, - render: function() { - return Backgrid.StringCell.prototype.render.apply(this, arguments); - }, - remove: function() { - let tableCols = this.model.top.get('columns'), - primary_key_col = this.model.get('columns'); - - if (primary_key_col) { - primary_key_col.off('pgadmin:multicolumn:updated'); - } - - this.stopListening(tableCols, 'remove' , self.removeColumn); - this.stopListening(tableCols, 'change:name' , self.resetColOptions); - - Backgrid.StringCell.prototype.remove.apply(this, arguments); - }, - }), - canDelete: true, canAdd: true, - control: Backform.MultiSelectAjaxControl.extend({ - defaults: _.extend( - {}, - Backform.NodeListByNameControl.prototype.defaults, - { - select2: { - multiple: true, - allowClear: true, - width: 'style', - placeholder: gettext('Select the column(s)'), - }, - } - ), - keyPathAccessor: function(obj, path) { - let res = obj; - if(_.isArray(res)) { - return _.map(res, function(o) { return o['column']; - }); - } - path = path.split('.'); - for (let path_val of path) { - if (_.isNull(res)) return null; - if (_.isEmpty(path_val)) continue; - if (!_.isUndefined(res[path_val])) res = res[path_val]; - } - return _.isObject(res) && !_.isArray(res) ? null : res; - }, - initialize: function() { - // Here we will decide if we need to call URL - // Or fetch the data from parent columns collection - let self = this; - if(this.model.handler) { - Backform.Select2Control.prototype.initialize.apply(this, arguments); - // Do not listen for any event(s) for existing constraint. - if (_.isUndefined(self.model.get('oid'))) { - let tableCols = self.model.top.get('columns'); - self.listenTo(tableCols, 'remove' , self.resetColOptions); - self.listenTo(tableCols, 'change:name', self.resetColOptions); - } - - self.custom_options(); - } else { - Backform.MultiSelectAjaxControl.prototype.initialize.apply(this, arguments); - } - self.model.get('columns').on('pgadmin:multicolumn:updated', function() { - self.render.apply(self); - }); - }, - resetColOptions: function() { - this.genResetColOptions(); - }, - custom_options: function() { - this.genCustomOptions(); - }, - onChange: function() { - let self = this, - model = this.model, - attrArr = this.field.get('name').split('.'), - name = attrArr.shift(), - vals = this.getValueFromDOM(), - collection = model.get(name), - removed = []; - - this.stopListening(this.model, 'change:' + name, this.render); - - /* - * Iterate through all the values, and find out how many are already - * present in the collection. - */ - collection.each(function(m) { - let column = m.get('column'), - idx = _.indexOf(vals, column); - - if (idx > -1) { - vals.splice(idx, 1); - } else { - removed.push(column); - } - }); - - /* - * Adding new values - */ - - _.each(vals, function(v) { - let m = new (self.field.get('model'))( - {column: v}, { silent: true, - top: self.model.top, - collection: collection, - handler: collection, - }); - - collection.add(m); - }); - - /* - * Removing unwanted! - */ - _.each(removed, function(v) { - collection.remove(collection.where({column: v})); - }); - - this.listenTo(this.model, 'change:' + name, this.render); - }, - remove: function() { - if(this.model.handler) { - let self = this, - tableCols = self.model.top.get('columns'); - self.stopListening(tableCols, 'remove' , self.resetColOptions); - self.stopListening(tableCols, 'change:name' , self.resetColOptions); - self.model.get('columns').off('pgadmin:multicolumn:updated'); - - Backform.Select2Control.prototype.remove.apply(this, arguments); - - } else { - Backform.MultiSelectAjaxControl.prototype.remove.apply(this, arguments); - } - }, - render: function() { - let index = this.model.get('index'); - if(!_.isUndefined(index) && index != '') { - let col = this.model.get('columns'); - col.reset([], {silent: true}); - } - return Backform.Select2Control.prototype.render.apply(this, arguments); - }, - }), - deps: ['index'], node: 'column', - model: pgBrowser.Node.Model.extend({ - defaults: { - column: undefined, - }, - validate: function() { - return null; - }, - }), - transform : function(data){ - let res = []; - if (data && _.isArray(data)) { - _.each(data, function(d) { - res.push({label: d.label, value: d.label, image:'icon-column'}); - }); - } - return res; - }, - select2:{allowClear:false}, - readonly: function(m) { - return this.checkReadOnly(m); - }, - disabled: function(m) { - // Disable if index is selected. - let index = m.get('index'); - return !(_.isUndefined(index) || index == ''); - }, - },{ - id: 'include', label: gettext('Include columns'), - type: 'array', group: gettext('Definition'), - editable: false, - canDelete: true, canAdd: true, mode: ['properties', 'create', 'edit'], - visible: function(m) { - /* In table properties, m.node_info is not available */ - m = m.top; - return (!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server) - && !_.isUndefined(m.node_info.server.version) && - m.node_info.server.version >= 110000); - }, - control: Backform.MultiSelectAjaxControl.extend({ - defaults: _.extend( - {}, - Backform.NodeListByNameControl.prototype.defaults, - { - select2: { - allowClear: false, - width: 'style', - multiple: true, - placeholder: gettext('Select the column(s)'), - }, - } - ), - initialize: function() { - // Here we will decide if we need to call URL - // Or fetch the data from parent columns collection - let self = this; - if(this.model.handler) { - Backform.Select2Control.prototype.initialize.apply(this, arguments); - // Do not listen for any event(s) for existing constraint. - if (_.isUndefined(self.model.get('oid'))) { - let tableCols = self.model.top.get('columns'); - self.listenTo(tableCols, 'remove' , self.resetColOptions); - self.listenTo(tableCols, 'change:name', self.resetColOptions); - } - - self.custom_options(); - } else { - Backform.MultiSelectAjaxControl.prototype.initialize.apply(this, arguments); - } - }, - resetColOptions: function() { - this.genResetColOptions(); - }, - custom_options: function() { - this.genCustomOptions(); - }, - }), - deps: ['index'], node: 'column', - readonly: function(m) { - return this.checkReadOnly(m); - }, - disabled: function(m) { - // Disable if index is selected. - let index = m.get('index'); - if(_.isUndefined(index) || index == '') { - return false; - } else { - setTimeout(function(){ - m.set('include', []); - },10); - return true; - } - }, - },{ - id: 'spcname', label: gettext('Tablespace'), - type: 'text', group: gettext('Definition'), - control: 'node-list-by-name', node: 'tablespace', - deps: ['index'], - select2:{allowClear:false}, - filter: function(m) { - // Don't show pg_global tablespace in selection. - return (m.label != 'pg_global'); - }, - disabled: function(m) { - // Disable if index is selected. - m = m.top || m; - let index = m.get('index'); - if(_.isUndefined(index) || index == '') { - return false; - } else { - setTimeout(function(){ - m.set('spcname', ''); - },10); - return true; - } - }, - },{ - id: 'index', label: gettext('Index'), - mode: ['create'], - type: 'text', group: gettext('Definition'), - control: Backform.NodeListByNameControl.extend({ - initialize:function() { - Backform.NodeListByNameControl.prototype.initialize.apply(this, arguments); - }, - }), - select2:{allowClear:true}, node: 'index', - readonly: function(m) { - // If we are in table edit mode then disable it - if (_.has(m, 'top') && !_.isUndefined(m.top) - && !m.top.isNew()) { - return true; - } - - // We can't update index of existing index constraint. - return !m.isNew(); - }, - // We will not show this field in Create Table mode - visible: function(m) { - return !_.isUndefined(m.top.node_info['table']); - }, - },{ - id: 'fillfactor', label: gettext('Fill factor'), deps: ['index'], - type: 'int', group: gettext('Definition'), allowNull: true, - disabled: function(m) { - // Disable if index is selected. - let index = m.get('index'); - if(_.isUndefined(index) || index == '') { - return false; - } else { - setTimeout(function(){ - m.set('fillfactor', null); - },10); - return true; - } - }, - },{ - id: 'condeferrable', label: gettext('Deferrable?'), - type: 'switch', group: gettext('Definition'), deps: ['index'], - readonly: function(m) { - return this.checkReadOnly(m); - }, - disabled: function(m) { - // Disable if index is selected. - let index = m.get('index'); - if(_.isUndefined(index) || index == '') { - return false; - } else { - setTimeout(function(){ - if(m.get('condeferrable')) - m.set('condeferrable', false); - },10); - return true; - } - }, - },{ - id: 'condeferred', label: gettext('Deferred?'), - type: 'switch', group: gettext('Definition'), - deps: ['condeferrable'], - readonly: function(m) { - return this.checkReadOnly(m); - }, - disabled: function(m) { - // Disable if condeferred is false or unselected. - if(m.get('condeferrable')) { - return false; - } else { - setTimeout(function(){ - if(m.get('condeferred')) - m.set('condeferred', false); - },10); - return true; - } - }, - }, - ], validate: function() { this.errorModel.clear(); // Clear parent's error as well diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js index 4bde610f7..7c2577949 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js @@ -19,7 +19,6 @@ define('pgadmin.node.mview', [ 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.node.schema.dir/child', 'pgadmin.node.schema.dir/schema_child_tree_node', 'sources/utils', - 'pgadmin.browser.server.privilege', ], function( gettext, url_for, $, pgAdmin, pgBrowser, schemaChild, schemaChildTreeNode, commonUtils diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js index 829ddca3b..aad666035 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js @@ -14,7 +14,7 @@ import ViewSchema from './view.ui'; define('pgadmin.node.view', [ 'sources/gettext', 'sources/url_for', 'pgadmin.browser', 'pgadmin.node.schema.dir/child', 'pgadmin.node.schema.dir/schema_child_tree_node', - 'pgadmin.browser.server.privilege', 'pgadmin.node.rule', + 'pgadmin.node.rule', ], function( gettext, url_for, pgBrowser, schemaChild, schemaChildTreeNode ) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js b/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js index 82b0bad47..34418bb2f 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js +++ b/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js @@ -19,7 +19,6 @@ define('pgadmin.node.database', [ 'sources/gettext', 'sources/url_for', 'jquery', 'sources/pgadmin', 'pgadmin.browser.utils', 'pgadmin.authenticate.kerberos', 'pgadmin.browser.collection', - 'pgadmin.browser.server.privilege', 'pgadmin.browser.server.variable', ], function(gettext, url_for, $, pgAdmin, pgBrowser, Kerberos) { if (!pgBrowser.Nodes['coll-database']) { diff --git a/web/pgadmin/browser/server_groups/servers/pgagent/steps/static/js/pga_jobstep.js b/web/pgadmin/browser/server_groups/servers/pgagent/steps/static/js/pga_jobstep.js index 341f979c5..676be3d98 100644 --- a/web/pgadmin/browser/server_groups/servers/pgagent/steps/static/js/pga_jobstep.js +++ b/web/pgadmin/browser/server_groups/servers/pgagent/steps/static/js/pga_jobstep.js @@ -11,9 +11,8 @@ import { getNodePgaJobStepSchema } from './pga_jobstep.ui'; import _ from 'lodash'; define('pgadmin.node.pga_jobstep', [ - 'sources/gettext', 'sources/url_for', 'pgadmin.browser', 'backform', - 'backgrid', 'pgadmin.backform', -], function(gettext, url_for, pgBrowser, Backform) { + 'sources/gettext', 'sources/url_for', 'pgadmin.browser', +], function(gettext, url_for, pgBrowser) { if (!pgBrowser.Nodes['coll-pga_jobstep']) { pgBrowser.Nodes['coll-pga_jobstep'] = @@ -93,37 +92,6 @@ define('pgadmin.node.pga_jobstep', [ } }, idAttribute: 'jstid', - schema: [{ - id: 'jstid', label: gettext('ID'), type: 'int', - cellHeaderClasses: 'width_percent_5', mode: ['properties'], - },{ - id: 'jstname', label: gettext('Name'), type: 'text', - cellHeaderClasses: 'width_percent_60', - },{ - id: 'jstenabled', label: gettext('Enabled?'), - type: 'switch', - },{ - id: 'jstkind', label: gettext('Kind'), type: 'switch', - options: { - 'onText': gettext('SQL'), 'offText': gettext('Batch'), - 'onColor': 'primary', 'offColor': 'primary', - }, control: Backform.SwitchControl, - },{ - id: 'jstconntype', label: gettext('Connection type'), - type: 'switch', deps: ['jstkind'], mode: ['properties'], - disabled: function(m) { return !m.get('jstkind'); }, - options: { - 'onText': gettext('Local'), 'offText': gettext('Remote'), - 'onColor': 'primary', 'offColor': 'primary', width: '65', - }, - },{ - id: 'jstonerror', label: gettext('On error'), cell: 'select2', - control: 'select2', options: [ - {'label': gettext('Fail'), 'value': 'f'}, - {'label': gettext('Success'), 'value': 's'}, - {'label': gettext('Ignore'), 'value': 'i'}, - ], select2: {allowClear: false}, - }], }), }); } diff --git a/web/pgadmin/browser/server_groups/servers/static/js/privilege.js b/web/pgadmin/browser/server_groups/servers/static/js/privilege.js deleted file mode 100644 index 28ebdda59..000000000 --- a/web/pgadmin/browser/server_groups/servers/static/js/privilege.js +++ /dev/null @@ -1,765 +0,0 @@ -///////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2022, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -////////////////////////////////////////////////////////////// - -import _ from 'lodash'; - -define(['sources/gettext', 'jquery', 'backbone', - 'backgrid', 'pgadmin.browser.node', 'sources/utils', 'pgadmin.browser.node.ui', -], function(gettext, $, Backbone, Backgrid, pgNode, commonUtils) { - /** - * Each Privilege, supporeted by an database object, will be represented - * using this Model. - * - * Defaults: - * privilege_type -> Name of the permission - * i.e. CREATE, TEMPORARY, CONNECT, etc. - * privilege -> Has privilege? (true/false) - * with_grant -> Has privilege with grant option (true/false) - **/ - let PrivilegeModel = pgNode.Model.extend({ - idAttribute: 'privilege_type', - defaults: { - privilege_type: undefined, - privilege: false, - with_grant: false, - }, - validate: function() { - return null; - }, - }); - - /** - * A database object has privileges item list (aclitem[]). - * - * This model represents the individual privilege item (aclitem). - * It has basically three properties: - * + grantee - Role to which that privilege applies to. - * Empty value represents to PUBLIC. - * + grantor - Grantor who has given this permission. - * + privileges - Privileges for that role. - **/ - let PrivilegeRoleModel = pgNode.PrivilegeRoleModel = pgNode.Model.extend({ - idAttribute: 'grantee', - defaults: { - grantee: undefined, - grantor: undefined, - privileges: undefined, - }, - keys: ['grantee', 'grantor'], - /* - * Each of the database object needs to extend this model, which should - * provide the type of privileges (it supports). - */ - privileges:[], - schema: [{ - id: 'grantee', label: gettext('Grantee'), type:'text', group: null, - editable: true, cellHeaderClasses: 'width_percent_40', - node: 'role', options_cached: false, - disabled : function(m) { - if (!(m instanceof Backbone.Model)) { - // This has been called during generating the header cell - return false; - } - return !( - m.top && m.top.node_info && - m.top.node_info.server.user.name == m.get('grantor') - ); - }, - transform: function() { - let res = - Backgrid.Extension.NodeListByNameCell.prototype.defaults.transform.apply( - this, arguments - ); - res.unshift({label: 'PUBLIC', value: 'PUBLIC'}); - return res; - }, - cell: Backgrid.Extension.NodeListByNameCell.extend({ - initialize: function(opts) { - let self = this, - override_opts = true; - - // We would like to override the original options, because - we - // should omit the existing role/user from the privilege cell. - // Because - the column is shared among all the cell, we can only - // override only once. - if (opts && opts.column && - opts.column instanceof Backbone.Model && - opts.column.get('options_cached')) { - override_opts = false; - } - Backgrid.Extension.NodeListByNameCell.prototype.initialize.apply( - self, arguments - ); - - // Let's override the options - if (override_opts) { - opts = self.column.get('options'); - self.column.set( - 'options', self.omit_selected_roles.bind(self, opts) - ); - } - - let rerender = function (m) { - let _self = this; - if ('grantee' in m.changed && this.model.cid != m.cid) { - setTimeout( - function() { - _self.render(); - }, 50 - ); - } - }.bind(this); - - // We would like to rerender all the cells of this type for this - // collection, because - we need to omit the newly selected roles - // form the list. Also, the render will be automatically called for - // the model represented by this cell, we will not do that again. - this.listenTo(self.model.collection, 'change', rerender, this); - this.listenTo(self.model.collection, 'remove', rerender, this); - }, - // Remove all the selected roles (though- not mine). - omit_selected_roles: function(opts, cell) { - let res = opts(cell), - selected = {}, - model = cell.model, - cid = model.cid, - // We need to check node_info values in parent when object is nested. - // eg: column level privileges in table dialog - // In this case node_info will not be avilable to column node as - // it is not loaded yet - node_info = (_.has(model.top, 'node_info') - && !_.isUndefined(model.top.node_info)) ? - model.top.node_info : - model.handler.top.node_info, - curr_user = node_info.server.user.name; - - model.collection.each(function(m) { - let grantee = m.get('grantee'); - - if (m.cid != cid && !_.isUndefined(grantee) && - curr_user == m.get('grantor')) { - selected[grantee] = m.cid; - } - }); - - res = _.filter(res, function(o) { - return !(o.value in selected); - }); - - return res; - }, - }), - },{ - id: 'privileges', label: gettext('Privileges'), - type: 'collection', model: PrivilegeModel, group: null, - cell: 'privilege', control: 'text', cellHeaderClasses: 'width_percent_40', - disabled : function(column) { - if (column instanceof Backbone.Collection) { - // This has been called during generating the header cell - return false; - } - return !( - this.node_info && - this.node_info.server.user.name == column.get('grantor') || - this.attributes.node_info.server.user.name == column.get('grantor') - ); - }, - },{ - id: 'grantor', label: gettext('Grantor'), type: 'text', readonly: true, - cell: 'node-list-by-name', node: 'role', - }], - - /* - * Initialize the model, which will transform the privileges string to - * collection of Privilege Model. - */ - initialize: function(attrs, opts) { - - pgNode.Model.prototype.initialize.apply(this, arguments); - - if (_.isNull(attrs)) { - this.set( - 'grantor', - opts && opts.top && opts.top.node_info && opts.top.node_info.server.user.name, - {silent: true} - ); - } - - /* - * Define the collection of the privilege supported by this model - */ - let self = this, - models = self.get('privileges'), - privileges = this.get('privileges') || {}; - - if (_.isArray(privileges)) { - privileges = new (pgNode.Collection)( - models, { - model: PrivilegeModel, - top: this.top || this, - handler: this, - silent: true, - parse: false, - }); - this.set('privileges', privileges, {silent: true}); - } - - let privs = {}; - _.each(self.privileges, function(p) { - privs[p] = { - 'privilege_type': p, 'privilege': false, 'with_grant': false, - }; - }); - - privileges.each(function(m) { - delete privs[m.get('privilege_type')]; - }); - - _.each(privs, function(p) { - privileges.add(p, {silent: true}); - }); - - self.on('change:grantee', self.granteeChanged); - privileges.on('change', function() { - self.trigger('change:privileges', self); - }); - - return self; - }, - - granteeChanged: function() { - let privileges = this.get('privileges'), - grantee = this.get('grantee'); - - // Reset all with grant options if grantee is public. - if (grantee == 'PUBLIC') { - privileges.each(function(m) { - m.set('with_grant', false, {silent: true}); - }); - } - }, - - toJSON: function() { - - let privileges = []; - - if (this.attributes && - !this.attributes['privileges']) { - return null; - } - - this.attributes['privileges'].each( - function(p) { - if (p.get('privilege')) { - privileges.push(p.toJSON()); - } - }); - - return { - 'grantee': this.get('grantee'), - 'grantor': this.get('grantor'), - 'privileges': privileges, - }; - }, - - validate: function() { - let errmsg = null, - msg; - - if (_.isUndefined(this.get('grantee'))) { - msg = gettext('A grantee must be selected.'); - this.errorModel.set('grantee', msg); - errmsg = msg; - } else { - this.errorModel.unset('grantee'); - } - - if (this.attributes && - this.attributes['privileges']) { - let anyPrivSelected = false; - this.attributes['privileges'].each( - function(p) { - if (p.get('privilege')) { - anyPrivSelected = true; - } - }); - - if (!anyPrivSelected) { - msg = gettext('At least one privilege should be selected.'); - this.errorModel.set('privileges', msg); - errmsg = errmsg || msg; - } else { - this.errorModel.unset('privileges'); - } - } - - return errmsg; - }, - }); - - /** - Custom cell editor for editing privileges. - */ - let PrivilegeCellEditor = Backgrid.Extension.PrivilegeCellEditor = - Backgrid.CellEditor.extend({ - tagName: 'div', - - // All available privileges in the PostgreSQL database server for - // generating the label for the specific Control - Labels: { - 'C': 'CREATE', - 'T': 'TEMPORARY', - 'c': 'CONNECT', - 'a': 'INSERT', - 'r': 'SELECT', - 'w': 'UPDATE', - 'd': 'DELETE', - 'D': 'TRUNCATE', - 'x': 'REFERENCES', - 't': 'TRIGGER', - 'U': 'USAGE', - 'X': 'EXECUTE', - }, - - template: _.template([ - '">', - ' ', - '
', - ' />', - ' ', - '
', - ' ', - ' ', - '
', - ' <%= enable_with_grant ? "" : \'disabled\'%>/>', - ' ', - '
', - ' ', - ''].join(' '), null, {variable: null}), - - events: { - 'change': 'privilegeChanged', - 'blur': 'lostFocus', - 'keydown': 'lostFocus', - }, - - render: function () { - this.$el.empty(); - this.$el.attr('tabindex', '1'); - this.$el.attr('target', this.elId); - - let collection = this.model.get(this.column.get('name')), - tbl = $('
').appendTo(this.$el), - self = this, - privilege = true, with_grant = true; - - // For each privilege generate html template. - // List down all the Privilege model. - let checkbox_id = _.uniqueId(); - collection.each(function(m) { - let d = m.toJSON(); - - privilege = (privilege && d.privilege); - with_grant = (with_grant && privilege && d.with_grant); - - _.extend( - d, { - 'target': self.cid, - 'header': false, - 'privilege_label': self.Labels[d.privilege_type], - 'with_grant': (self.model.get('grantee') != 'PUBLIC' && d.with_grant), - 'enable_with_grant': (self.model.get('grantee') != 'PUBLIC' && d.privilege), - 'checkbox_id': d.privilege_type + '' + checkbox_id, - }); - privilege = (privilege && d.privilege); - with_grant = (with_grant && privilege && d.with_grant); - tbl.append(self.template(d)); - }); - - if (collection.length > 1) { - // Preprend the ALL controls on that table - tbl.prepend( - self.template({ - 'target': self.cid, - 'privilege_label': 'ALL', - 'privilege_type': 'ALL', - 'privilege': privilege, - 'with_grant': (self.model.get('grantee') != 'PUBLIC' && with_grant), - 'enable_with_grant': (self.model.get('grantee') != 'PUBLIC' && privilege), - 'header': true, - 'checkbox_id': 'all' + '' + checkbox_id, - })); - } - self.$el.find('input[type=checkbox]').first().trigger('focus'); - // Since blur event does not bubble we need to explicitly call parent's blur event. - $(self.$el.find('input[type=checkbox]')).on('blur',function() { - self.$el.trigger('blur'); - }); - - // Make row visible in when entering in edit mode. - $(self.$el).pgMakeVisible('backform-tab'); - - self.delegateEvents(); - - return this; - }, - - /* - * Listen to the checkbox value change and update the model accordingly. - */ - privilegeChanged: function(ev) { - - if (ev && ev.target) { - /* - * We're looking for checkboxes only. - */ - let $el = $(ev.target), - privilege_type = $el.attr('privilege'), - type = $el.attr('name'), - checked = $el.prop('checked'), - $tr = $el.closest('tr'), - $tbl = $tr.closest('table'), - collection = this.model.get('privileges'), - grantee = this.model.get('grantee'), $allGrants, - $allPrivileges, $elGrant; - - this.undelegateEvents(); - /* - * If the checkbox selected/deselected is for 'ALL', we will select all - * the checkbox for each privilege. - */ - if (privilege_type == 'ALL') { - let allPrivilege, allWithGrant; - - $elGrant = $tr.find('input[name=with_grant]'); - $allPrivileges = $tbl.find( - 'input[name=privilege][privilege!=\'ALL\']' - ); - $allGrants = $tbl.find( - 'input[name=with_grant][privilege!=\'ALL\']' - ); - - if (type == 'privilege') { - /* - * We clicked the privilege checkbox, and not checkbox for with - * grant options. - */ - allPrivilege = checked; - allWithGrant = false; - - if (checked) { - $allPrivileges.prop('checked', true); - - /* - * We have clicked the ALL checkbox, we should be able to select - * the grant options too. - */ - if (grantee == 'PUBLIC') { - $allGrants.prop('disabled', true); - $elGrant.prop('disabled', true); - } else { - $allGrants.prop('disabled', false); - $elGrant.prop('disabled', false); - } - - } else { - /* - * ALL checkbox has been deselected, hence - we need to make - * sure. - * 1. Deselect all the privileges checkboxes - * 2. Deselect and disable all with grant privilege checkboxes. - * 3. Deselect and disable the checkbox for ALL with grant privilege. - */ - $allPrivileges.prop('checked', false); - $elGrant.prop('checked', false); - $allGrants.prop('checked', false); - $elGrant.prop('disabled', true); - $allGrants.prop('disabled', true); - } - } else { - /* - * We were able to click the ALL with grant privilege checkbox, - * that means, privilege for Privileges are true. - * - * We need to select/deselect all the with grant options - * checkboxes, based on the current value of the ALL with grant - * privilege checkbox. - */ - allPrivilege = true; - allWithGrant = checked; - $allGrants.prop('checked', checked); - } - - /* - * Set the values for each Privilege Model. - */ - collection.each(function(m) { - m.set( - {'privilege': allPrivilege, 'with_grant': allWithGrant} - ); - }); - } else { - /* - * Particular privilege has been selected/deselected, which can be - * identified using the privilege="X" attribute. - */ - let attrs = {}; - - $tbl = $tr.closest('table'); - $allPrivileges = $tbl.find( - 'input[name=privilege][privilege=\'ALL\']' - ); - $allGrants = $tbl.find( - 'input[name=with_grant][privilege=\'ALL\']' - ); - - attrs[type] = checked; - - if (type == 'privilege') { - $elGrant = ($el.closest('tr')).find('input[name=with_grant]'); - if (!checked) { - attrs['with_grant'] = false; - - $elGrant.prop('checked', false).prop('disabled', true); - $allPrivileges.prop('checked', false); - $allGrants.prop('disabled', true); - $allGrants.prop('checked', false); - } else if (grantee != 'PUBLIC') { - $elGrant.prop('disabled', false); - } - } else if (!checked) { - $allGrants.prop('checked', false); - } - collection.get(privilege_type).set(attrs); - - if (checked) { - $allPrivileges = $tbl.find( - 'input[name=privilege][privilege!=\'ALL\']:checked' - ); - - if ($allPrivileges.length > 1 && - $allPrivileges.length == collection.models.length) { - - $allPrivileges.prop('checked', true); - - if (type == 'with_grant') { - $allGrants = $tbl.find( - 'input[name=with_grant][privilege!=\'ALL\']:checked' - ); - if ($allGrants.length == collection.models.length) { - $allGrants.prop('disabled', false); - $allGrants.prop('checked', true); - } - } else if (grantee != 'PUBLIC') { - $allGrants.prop('disabled', false); - } - } - } - } - this.model.trigger('change', this.model); - - let anySelected = false, - msg = null; - - collection.each(function(m) { - anySelected = anySelected || m.get('privilege'); - }); - - if (anySelected) { - this.model.errorModel.unset('privileges'); - if (this.model.errorModel.has('grantee')) { - msg = this.model.errorModel.get('grantee'); - } - } else { - this.model.errorModel.set( - 'privileges', gettext('At least one privilege should be selected.') - ); - msg = gettext('At least one privilege should be selected.'); - } - if (msg) { - this.model.collection.trigger( - 'pgadmin-session:model:invalid', msg, this.model - ); - } else { - this.model.collection.trigger( - 'pgadmin-session:model:valid', this.model - ); - } - } - this.delegateEvents(); - }, - - lostFocus: function(ev) { - /* - * We lost the focus, it's time for us to exit the editor. - */ - let self = this, - /* - * Function to determine whether one dom element is descendant of another - * dom element. - */ - isDescendant = function (parent, child) { - let node = child.parentNode; - while (node != null) { - if (node == parent) { - return true; - } - node = node.parentNode; - } - return false; - }, - model = this.model, - column = this.column, - command = new Backgrid.Command(ev), - coll = this.model.get(this.column.get('name')); - - if (ev.key == 'Tab'){ - commonUtils.handleKeyNavigation(event); - } - - if (command.moveUp() || command.moveDown() || command.save() || command.cancel() || - (command.moveLeft() && ev.target.name === 'privilege' && $(ev.target).attr('privilege') === 'ALL')) { - // undo - ev.stopPropagation(); - model.trigger('backgrid:edited', model, column, command); - return; - } else if (command.moveRight()) { - // If we are at the last privilege then we should move to next cell - if (coll.last().get('privilege_type') === $(ev.target).attr('privilege')) { - if ((ev.target.name === 'privilege' && !ev.target.checked ) || - $(ev.target).attr('name') === 'with_grant') { - ev.stopPropagation(); - model.trigger('backgrid:edited', model, column, command); - return; - } - } - } - - /* - * Between leaving the old element focus and entering the new element focus the - * active element is the document/body itself so add timeout to get the proper - * focused active element. - */ - setTimeout(function() { - /* - * Do not close the control if user clicks outside dialog window, - * only close the row if user clicks on add button or on another row, - * if user clicks somewhere else then we will get tagName as 'BODY' - * or 'WINDOW' - */ - let is_active_element = document.activeElement.tagName == 'DIV' || - document.activeElement.tagName == 'BUTTON'; - - if (is_active_element && self.$el[0] != document.activeElement && - !isDescendant(self.$el[0], document.activeElement)) { - let m = self.model; - m.trigger('backgrid:edited', m, self.column, new Backgrid.Command(ev)); - }},10); - }, - }); - - /* - * This will help us transform the privileges value in proper format to be - * displayed in the cell. - */ - let PrivilegeCellFormatter = Backgrid.Extension.PrivilegeCellFormatter = - function () {/*This is intentional (SonarQube)*/}; - _.extend(PrivilegeCellFormatter.prototype, { - notation: { - 'CREATE' : 'C', - 'TEMPORARY' : 'T', - 'CONNECT' : 'c', - 'INSERT' : 'a', - 'SELECT' : 'r', - 'UPDATE' : 'w', - 'DELETE' : 'd', - 'TRUNCATE' : 'D', - 'REFERENCES' : 'x', - 'TRIGGER' : 't', - 'USAGE' : 'U', - 'EXECUTE' : 'X', - }, - /** - * Takes a raw value from a model and returns an optionally formatted - * string for display. - */ - fromRaw: function (rawData) { - let res = ''; - - if (rawData instanceof Backbone.Collection) { - rawData.each(function(m) { - if (m.get('privilege')) { - res += m.get('privilege_type'); - if (m.get('with_grant')) { - res += '*'; - } - } - }); - } - return res; - }, - }); - - /* - * PrivilegeCell for rendering and taking input for the privileges. - */ - Backgrid.Extension.PrivilegeCell = Backgrid.Cell.extend({ - className: 'edit-cell', - formatter: PrivilegeCellFormatter, - editor: PrivilegeCellEditor, - - initialize: function () { - let self = this; - Backgrid.Cell.prototype.initialize.apply(this, arguments); - - self.model.on('change:grantee', function() { - if (!self.$el.hasClass('editor')) { - /* - * Add time out before render; As we might want to wait till model - * is updated by PrivilegeRoleModel:granteeChanged. - */ - setTimeout(function() { - self.render(); - },10); - } - }); - }, - - events: { - 'click': 'enterEditMode', - 'keydown': 'saveOrCancel', - }, - - saveOrCancel: function (e) { - let model = this.model; - let column = this.column; - let command = new Backgrid.Command(e); - - if (command.moveUp() || command.moveDown() || command.moveLeft() || command.moveRight() || - command.save()) { - e.preventDefault(); - e.stopPropagation(); - model.trigger('backgrid:edited', model, column, command); - } - // esc - else if (command.cancel()) { - // undo - e.stopPropagation(); - model.trigger('backgrid:edited', model, column, command); - } - }, - }); - - return PrivilegeRoleModel; - -}); diff --git a/web/pgadmin/browser/server_groups/servers/static/js/server.js b/web/pgadmin/browser/server_groups/servers/static/js/server.js index b067a4f0e..e77111b3c 100644 --- a/web/pgadmin/browser/server_groups/servers/static/js/server.js +++ b/web/pgadmin/browser/server_groups/servers/static/js/server.js @@ -18,7 +18,6 @@ define('pgadmin.node.server', [ 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.user_management.current_user', 'pgadmin.authenticate.kerberos', - 'pgadmin.browser.server.privilege', ], function( gettext, url_for, $, pgAdmin, pgBrowser, current_user, Kerberos, diff --git a/web/pgadmin/browser/server_groups/servers/static/js/variable.js b/web/pgadmin/browser/server_groups/servers/static/js/variable.js deleted file mode 100644 index 30191e1db..000000000 --- a/web/pgadmin/browser/server_groups/servers/static/js/variable.js +++ /dev/null @@ -1,490 +0,0 @@ -///////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2022, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -////////////////////////////////////////////////////////////// - -import _ from 'lodash'; - -define([ - 'sources/gettext', 'jquery', 'backbone', 'backform', 'backgrid', - 'pgadmin.browser.node', 'pgadmin.browser.node.ui', -], -function(gettext, $, Backbone, Backform, Backgrid, pgNode) { - - /* - * cellFunction for variable control. - * This function returns cell class depending on vartype. - */ - let cellFunction = function(model) { - let self = this, - name = model.get('name'), - availVariables = {}; - - self.collection.each(function(col) { - if (col.get('name') == 'name') { - availVariables = col.get('availVariables'); - } - }); - - let variable = name ? availVariables[name]: undefined, - value = model.get('value'); - - switch(variable && variable.vartype) { - case 'bool': - /* - * bool cell and variable cannot be stateless (i.e undefined). - * It should be either true or false. - */ - - model.set('value', !!model.get('value'), {silent: true}); - - return Backgrid.Extension.SwitchCell; - case 'enum': - model.set({'value': value}, {silent:true}); - var options = [], - enumVals = variable && variable.enumvals; - - _.each(enumVals, function(enumVal) { - options.push([enumVal, enumVal]); - }); - - return Backgrid.Extension.Select2Cell.extend({optionValues: options}); - case 'integer': - if (!_.isNaN(parseInt(value))) { - model.set({'value': parseInt(value)}, {silent:true}); - } else { - model.set({'value': undefined}, {silent:true}); - } - return Backgrid.IntegerCell; - case 'real': - if (!_.isNaN(parseFloat(value))) { - model.set({'value': parseFloat(value)}, {silent:true}); - } else { - model.set({'value': undefined}, {silent:true}); - } - return Backgrid.NumberCell.extend({decimals: 0}); - case 'string': - return Backgrid.StringCell; - default: - model.set({'value': undefined}, {silent:true}); - return Backgrid.Cell; - } - }; - - /* - * This row will define behaviour or value column cell depending upon - * variable name. - */ - let VariableRow = Backgrid.Row.extend({ - modelDuplicateClass: 'bg-model-duplicate', - - initialize: function () { - Backgrid.Row.prototype.initialize.apply(this, arguments); - let self = this; - self.model.on('change:name', function() { - setTimeout(function() { - self.columns.each(function(col) { - if (col.get('name') == 'value') { - // Reset old value - self.model.set({'value': undefined}, {silent:true}); - let idx = self.columns.indexOf(col), - cf = col.get('cellFunction'), - cell = new (cf.apply(col, [self.model]))({ - column: col, - model: self.model, - }), - oldCell = self.cells[idx]; - oldCell.remove(); - self.cells[idx] = cell; - self.render(); - } - - }); - }, 10); - }); - self.listenTo(self.model, 'pgadmin-session:model:duplicate', self.modelDuplicate); - self.listenTo(self.model, 'pgadmin-session:model:unique', self.modelUnique); - }, - modelDuplicate: function() { - $(this.el).removeClass('new'); - $(this.el).addClass(this.modelDuplicateClass); - }, - modelUnique: function() { - $(this.el).removeClass(this.modelDuplicateClass); - }, - - }); - /** - * VariableModel used to represent configuration parameters (variables tab) - * for database objects. - **/ - let VariableModel = pgNode.VariableModel = pgNode.Model.extend({ - keys: ['name'], - defaults: { - name: undefined, - value: undefined, - role: null, - database: null, - }, - schema: [ - { - id: 'name', label: gettext('Name'), type:'text', cellHeaderClasses: 'width_percent_30', - editable: function(m) { - return (m instanceof Backbone.Collection) ? true : m.isNew(); - }, - cell: Backgrid.Extension.NodeAjaxOptionsCell.extend({ - initialize: function() { - Backgrid.Extension.NodeAjaxOptionsCell.prototype.initialize.apply(this, arguments); - - // Immediately process options as we need them before render. - - let opVals = _.clone(this.optionValues || - (_.isFunction(this.column.get('options')) ? - (this.column.get('options'))(this) : - this.column.get('options'))); - - this.column.set('options', opVals); - }, - }), - url: 'vopts', - select2: { allowClear: false }, - transform: function(vars, cell) { - let res = [], - availVariables = {}; - - _.each(vars, function(v) { - res.push({ - 'value': v.name, - 'image': undefined, - 'label': v.name, - }); - availVariables[v.name] = v; - }); - - cell.column.set('availVariables', availVariables); - return res; - }, - }, - { - id: 'value', label: gettext('Value'), type: 'text', editable: true, - cellFunction: cellFunction, cellHeaderClasses: 'width_percent_40', - }, - {id: 'database', label: gettext('Database'), type: 'text', editable: true, - node: 'database', cell: Backgrid.Extension.NodeListByNameCell, - }, - {id: 'role', label: gettext('Role'), type: 'text', editable: true, - node: 'role', cell: Backgrid.Extension.NodeListByNameCell}, - ], - toJSON: function() { - let d = Backbone.Model.prototype.toJSON.apply(this); - - // Remove not defined values from model values. - // i.e. - // role, database - if (_.isUndefined(d.database) || _.isNull(d.database)) { - delete d.database; - } - - if (_.isUndefined(d.role) || _.isNull(d.role)) { - delete d.role; - } - - return d; - }, - validate: function() { - let msg = null; - if (_.isUndefined(this.get('name')) || - _.isNull(this.get('name')) || - String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Please select a parameter name.'); - this.errorModel.set('name', msg); - } else if (_.isUndefined(this.get('value')) || - _.isNull(this.get('value')) || - String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Please enter a value for the parameter.'); - this.errorModel.set('value', msg); - this.errorModel.unset('name'); - } else { - this.errorModel.unset('name'); - this.errorModel.unset('value'); - } - - return msg; - }, - }); - - /** - * Variable Tab Control to set/update configuration values for database object. - * - **/ - Backform.VariableCollectionControl = - Backform.UniqueColCollectionControl.extend({ - - hasDatabase: false, - hasRole: false, - - initialize: function(opts) { - let self = this, - keys = ['name']; - - /* - * Read from field schema whether user wants to use database and role - * fields in Variable control. - */ - self.hasDatabase = opts.field.get('hasDatabase'); - self.hasRole = opts.field.get('hasRole'); - - // Update unique coll field based on above flag status. - if (self.hasDatabase) { - keys.push('database'); - } else if (self.hasRole) { - keys.push('role'); - } - // Overriding the uniqueCol in the field - if (opts && opts.field) { - if (opts.field instanceof Backform.Field) { - opts.field.set({ - model: pgNode.VariableModel.extend({keys:keys}), - }, - { - silent: true, - }); - } else { - opts.field.extend({ - model: pgNode.VariableModel.extend({keys:keys}), - }); - } - } - - Backform.UniqueColCollectionControl.prototype.initialize.apply( - self, arguments - ); - - self.availVariables = {}; - - let gridCols = ['name', 'value']; - - if (self.hasDatabase) { - gridCols.push('database'); - } - - if (self.hasRole) { - gridCols.push('role'); - } - - self.gridSchema = Backform.generateGridColumnsFromModel( - self.field.get('node_info'), VariableModel.extend({keys:keys}), 'edit', gridCols, self.field.get('schema_node') - ); - - // Make sure - we do have the data for variables - self.getVariables(); - }, - /* - * Get the variable data for this node. - */ - getVariables: function() { - let self = this, - url = this.field.get('url'), - m = self.model; - - if (!this.field.get('version_compatible')) - return; - - if (url && !m.isNew()) { - let node = self.field.get('node'), - node_data = self.field.get('node_data'), - node_info = self.field.get('node_info'), - full_url = node.generate_url.apply( - node, [ - null, url, node_data, true, node_info, - ]), - data, - isTracking = self.collection.trackChanges; - - if (isTracking) { - self.collection.stopSession(); - } - m.trigger('pgadmin-view:fetching', m, self.field); - - $.ajax({ - async: false, - url: full_url, - }) - .done(function (res) { - data = res.data; - }) - .fail(function() { - m.trigger('pgadmin-view:fetch:error', m, self.field); - }); - m.trigger('pgadmin-view:fetched', m, self.field); - - if (data && _.isArray(data)) { - self.collection.reset(data, {silent: true}); - } - /* - * Make sure - new data will be taken care by the session management - */ - if (isTracking) { - self.collection.startNewSession(); - } - } - }, - - showGridControl: function(data) { - - let self = this, - titleTmpl = _.template([ - '
', - '<%-label%>', - '', - '
'].join('\n')), - $gridBody = - $('
').append( - titleTmpl(data) - ); - - // Clean up existing grid if any (in case of re-render) - if (self.grid) { - self.grid.remove(); - } - - let gridSchema = _.clone(this.gridSchema); - - _.each(gridSchema.columns, function(col) { - if (col.name == 'value') { - col.availVariables = self.availVariables; - } - }); - - // Insert Delete Cell into Grid - if (data.disabled && data.canDelete) { - gridSchema.columns.unshift({ - name: 'pg-backform-delete width_percent_5', label: '', - cell: Backgrid.Extension.DeleteCell, - editable: false, cell_priority: -1, - }); - } - - // Change format of each of the data - // Because - data coming from the server is in string format - self.collection.each(function(model) { - let name = model.get('name'), val; - - if (name in self.availVariables) { - switch(self.availVariables[name].vartype) { - case 'real': - val = parseFloat(model.get('value')); - model.set('value', (isNaN(val) ? undefined : val), {silent: true}); - - break; - case 'integer': - val = parseInt(model.get('value')); - model.set('value', (isNaN(val) ? undefined : val), {silent: true}); - - break; - default: - break; - } - } - }); - - // Initialize a new Grid instance - let grid = self.grid = new Backgrid.Grid({ - columns: gridSchema.columns, - collection: self.collection, - row: VariableRow, - className: 'backgrid table presentation table-bordered table-noouter-border table-hover', - }); - self.$grid = grid.render().$el; - - $gridBody.append(self.$grid); - - // Add button callback - if (!(data.disabled || !data.canAdd)) { - $gridBody.find('button.add').first().on('click',(e) => { - e.preventDefault(); - let canAddRow = _.isFunction(data.canAddRow) ? - data.canAddRow.apply(self, [self.model]) : true; - if (canAddRow) { - - let allowMultipleEmptyRows = !!self.field.get('allowMultipleEmptyRows'); - - // If allowMultipleEmptyRows is not set or is false then don't allow second new empty row. - // There should be only one empty row. - if (!allowMultipleEmptyRows && self.collection) { - let isEmpty = false; - self.collection.each(function(model) { - let modelValues = []; - _.each(model.attributes, function(val) { - modelValues.push(val); - }); - if(!_.some(modelValues, _.identity)) { - isEmpty = true; - } - }); - if(isEmpty) { - return false; - } - } - - $(grid.body.$el.find($('tr.new'))).removeClass('new'); - let m = new (data.model) (null, { - silent: true, - handler: self.collection, - top: self.model.top || self.model, - collection: self.collection, - node_info: self.model.node_info, - }); - self.collection.add(m); - - let idx = self.collection.indexOf(m), - newRow = grid.body.rows[idx].$el; - - newRow.addClass('new'); - $(newRow).pgMakeVisible('backform-tab'); - - return false; - } - }); - } - - // Render node grid - return $gridBody; - }, - - addVariable: function(ev) { - ev.preventDefault(); - - let self = this, - m = new (self.field.get('model'))( - self.headerData.toJSON(), { - silent: true, top: self.collection.top, - handler: self.collection, - }), - coll = self.model.get(self.field.get('name')); - - coll.add(m); - - let idx = coll.indexOf(m); - - // idx may not be always > -1 because our UniqueColCollection may - // remove 'm' if duplicate value found. - if (idx > -1) { - self.$grid.find('.new').removeClass('new'); - - let newRow = self.grid.body.rows[idx].$el; - - newRow.addClass('new'); - $(newRow).pgMakeVisible('backform-tab'); - } - - return false; - }, - }); - - return VariableModel; -}); diff --git a/web/pgadmin/browser/server_groups/servers/tablespaces/static/js/tablespace.js b/web/pgadmin/browser/server_groups/servers/tablespaces/static/js/tablespace.js index 7a744ab46..2663c2812 100644 --- a/web/pgadmin/browser/server_groups/servers/tablespaces/static/js/tablespace.js +++ b/web/pgadmin/browser/server_groups/servers/tablespaces/static/js/tablespace.js @@ -15,8 +15,7 @@ import _ from 'lodash'; define('pgadmin.node.tablespace', [ 'sources/gettext', 'sources/url_for', - 'pgadmin.browser', 'pgadmin.browser.collection', 'pgadmin.browser.node.ui', - 'pgadmin.browser.server.privilege', + 'pgadmin.browser', 'pgadmin.browser.collection', ], function( gettext, url_for, pgBrowser ) { diff --git a/web/pgadmin/browser/static/js/collection.js b/web/pgadmin/browser/static/js/collection.js index 998260ee6..a66acb12e 100644 --- a/web/pgadmin/browser/static/js/collection.js +++ b/web/pgadmin/browser/static/js/collection.js @@ -12,8 +12,7 @@ import _ from 'lodash'; define([ 'sources/gettext', 'sources/pgadmin', 'sources/browser/generate_url', - 'pgadmin.backform', 'pgadmin.backgrid', - 'pgadmin.browser.node', 'backgrid.select.all', + 'pgadmin.browser.node', ], function(gettext, pgAdmin, generateUrl) { let pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {}; diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js index d7f9e435a..2c5ab2062 100644 --- a/web/pgadmin/browser/static/js/node.js +++ b/web/pgadmin/browser/static/js/node.js @@ -10,17 +10,16 @@ import {getNodeView, removeNodeView} from './node_view'; import Notify from '../../../static/js/helpers/Notifier'; import _ from 'lodash'; -import { pgHandleItemError } from '../../../static/js/utils'; define('pgadmin.browser.node', [ 'sources/gettext', 'jquery', 'sources/pgadmin', 'backbone', 'pgadmin.browser.datamodel', - 'backform', 'sources/browser/generate_url', 'pgadmin.help', 'sources/utils', - 'pgadmin.browser.utils', 'pgadmin.backform', + 'sources/browser/generate_url', 'pgadmin.help', 'sources/utils', + 'pgadmin.browser.utils', ], function( gettext, $, pgAdmin, Backbone, pgBrowser, - Backform, generateUrl, help, + generateUrl, help, commonUtils ) { @@ -294,210 +293,6 @@ define('pgadmin.browser.node', [ return true; } }, - /////// - // Generate a Backform view using the node's model type - // - // Used to generate view for the particular node properties, edit, - // creation. - getView: function(item, type, el, node, formType, callback, ctx, cancelFunc) { - let that = this; - - if (!this.type || this.type == '') - // We have no information, how to generate view for this type. - return null; - - if (this.model) { - // This will be the URL, used for object manipulation. - // i.e. Create, Update in these cases - let urlBase = this.generate_url(item, type, node, false, null, that.url_jump_after_node); - - if (!urlBase) - // Ashamed of myself, I don't know how to manipulate this - // node. - return null; - - let attrs = {}; - - // In order to get the object data from the server, we must set - // object-id in the model (except in the create mode). - if (type !== 'create') { - attrs[this.model.idAttribute || this.model.prototype.idAttribute || - 'id'] = node._id; - } - - // We know - which data model to be used for this object. - let info = pgBrowser.tree.getTreeNodeHierarchy(item), - newModel = new(this.model.extend({ - urlRoot: urlBase, - }))( - attrs, { - node_info: info, - } - ), - fields = Backform.generateViewSchema( - info, newModel, type, this, node - ); - - if (type == 'create' || type == 'edit') { - - if (callback && ctx) { - callback = callback.bind(ctx); - } else { - callback = function() { - console.warn( - 'Broke something!!! Why we don\'t have the callback or the context???' - ); - }; - } - - let onSessionInvalid = function(msg) { - let alertMessage = ` - `; - - if (!_.isUndefined(that.statusBar)) { - that.statusBar.html(alertMessage).css('visibility', 'visible'); - that.statusBar.find('a.close-error').bind('click', function() { - this.empty().css('visibility', 'hidden'); - }.bind(that.statusBar)); - } - - let sessHasChanged = false; - if(this.sessChanged && this.sessChanged()){ - sessHasChanged = true; - } - callback(true, sessHasChanged); - - return true; - }; - - let onSessionValidated = function(sessHasChanged) { - - if (!_.isUndefined(that.statusBar)) { - that.statusBar.empty().css('visibility', 'hidden'); - } - - callback(false, sessHasChanged); - }; - - callback(false, false); - - newModel.on('pgadmin-session:valid', onSessionValidated); - newModel.on('pgadmin-session:invalid', onSessionInvalid); - } - - let view; - // 'schema' has the information about how to generate the form. - if (_.size(fields)) { - // This will contain the actual view - - if (formType == 'fieldset') { - // It is used to show, edit, create the object in the - // properties tab. - view = new Backform.Accordian({ - el: el, - model: newModel, - schema: fields, - }); - } else { - // This generates a view to be used by the node dialog - // (for create/edit operation). - view = new Backform.Dialog({ - el: el, - model: newModel, - schema: fields, - }); - } - - let setFocusOnEl = function() { - let container = $(el).find('.tab-content:first > .tab-pane.active:first'); - commonUtils.findAndSetFocus(container); - }; - - if (!newModel.isNew()) { - // This is definetely not in create mode - let msgDiv = '
' + - gettext('Retrieving data from the server...') + '
', - $msgDiv = $(msgDiv); - let timer = setTimeout(function(_ctx) { - // notify user if request is taking longer than 1 second - - if (!_.isUndefined(_ctx)) { - $msgDiv.appendTo(_ctx); - } - }, 1000, ctx); - - - let fetchAjaxHook = function() { - newModel.fetch({ - success: function() { - // Clear timeout and remove message - clearTimeout(timer); - $msgDiv.addClass('d-none'); - - // We got the latest attributes of the object. Render the view - // now. - view.render(); - setFocusOnEl(); - newModel.startNewSession(); - }, - error: function(model, xhr, options) { - let _label = that && item ? - pgBrowser.tree.getTreeNodeHierarchy( - item - )[that.type].label : ''; - pgBrowser.Events.trigger( - 'pgadmin:node:retrieval:error', 'properties', - xhr, options.textStatus, options.errorThrown, item - ); - if (!pgHandleItemError(xhr, { - item: item, - info: info, - } - )) { - Notify.pgNotifier( - options.textStatus, xhr, - gettext('Error retrieving properties - %s', options.errorThrown || _label), - function(msg) { - if(msg === 'CRYPTKEY_SET') { - fetchAjaxHook(); - } else { - console.warn(arguments); - } - } - ); - } - // Close the panel (if could not fetch properties) - if (cancelFunc) { - cancelFunc(); - } - }, - }); - }; - - fetchAjaxHook(); - } else { - // Yay - render the view now! - view.render(); - setFocusOnEl(); - newModel.startNewSession(); - } - } - - return view; - } - - return null; - }, addUtilityPanel: function(width, height, docker) { let body = window.document.body, el = document.createElement('div'), @@ -1182,8 +977,6 @@ define('pgadmin.browser.node', [ tree = pgAdmin.Browser.tree, j = panel.$container.find('.obj_properties').first(), view = j.data('obj-view'), - content = $('
') - .addClass('pg-prop-content col-12'), confirm_close = true; // Handle key press events for Cancel, save and help button @@ -1220,70 +1013,11 @@ define('pgadmin.browser.node', [ }); }, 200); // wait for panel tab to render - // Template function to create the status bar - let createStatusBar = function(location) { - let statusBar = $('
').addClass( - 'pg-prop-status-bar' - ).appendTo(j); - statusBar.css('visibility', 'hidden'); - if (location == 'header') { - statusBar.appendTo(that.header); - } else { - statusBar.prependTo(that.footer); - } - that.statusBar = statusBar; - return statusBar; - }.bind(panel), - // Template function to create the button-group - createButtons = function(buttons, location, extraClasses) { - // Arguments must be non-zero length array of type - // object, which contains following attributes: - // label, type, extraClasses, register - if (buttons && _.isArray(buttons) && buttons.length > 0) { - // All buttons will be created within a single - // div area. - let btnGroup = - $('
'), - // Template used for creating a button - tmpl = _.template([ - '', - ].join(' ')); - if (location == 'header') { - btnGroup.appendTo(that.header); - } else { - btnGroup.appendTo(that.footer); - } - if (extraClasses) { - btnGroup.addClass(extraClasses); - } - _.each(buttons, function(btn) { - // Create the actual button, and append to - // the group div - - // icon may not present for this button - if (!btn.icon) { - btn.icon = ''; - } - let b = $(tmpl(btn)); - btnGroup.append(b); - // Register is a callback to set callback - // for certain operation for this button. - btn.register(b); - }); - return btnGroup; - } - return null; - }.bind(panel), - // Callback to show object properties - properties = function() { + // Callback to show object properties + let properties = function() { // Avoid unnecessary reloads let i = tree.selected(), - d = i && tree.itemData(i), treeHierarchy = tree.getTreeNodeHierarchy(i); // Cache the current IDs for next time @@ -1291,106 +1025,11 @@ define('pgadmin.browser.node', [ /* Remove any dom rendered by getNodeView */ removeNodeView(j[0]); - if(that.getSchema) { - let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item); - getNodeView( - that.type, treeNodeInfo, 'properties', data, 'tab', j[0], this, onEdit - ); - return; - } - - if (!content.hasClass('has-pg-prop-btn-group')) - content.addClass('has-pg-prop-btn-group'); - - // We need to release any existing view, before - // creating new view. - if (view) { - // Release the view - view.remove({ - data: true, - internal: true, - silent: true, - }); - // Deallocate the view - // delete view; - view = null; - // Reset the data object - j.data('obj-view', null); - } - // Make sure the HTML element is empty. - j.empty(); - that.header = $('
').addClass( - 'pg-prop-header' - ).appendTo(j); - that.footer = $('
').addClass( - 'pg-prop-footer' - ).appendTo(j); - - // Create a view to show the properties in fieldsets - view = that.getView(item, 'properties', content, data, 'fieldset', undefined, j); - if (view) { - // Save it for release it later - j.data('obj-view', view); - - // Create proper buttons - - let buttons = []; - - buttons.push({ - label: gettext('Edit'), - type: 'edit', - tooltip: gettext('Edit'), - extraClasses: ['btn', 'btn-primary', 'pull-right', 'm-1'], - icon: 'fa fa-sm fa-pencil-alt', - disabled: _.isFunction(that.canEdit) ? !that.canEdit.apply(that, [d, i]) : !that.canEdit, - register: function(btn) { - btn.on('click',() => { - onEdit(); - }); - }, - }); - - buttons.push({ - label: '', - type: 'help', - tooltip: gettext('SQL help for this object type.'), - extraClasses: ['btn-primary-icon', 'btn-primary-icon', 'm-1'], - icon: 'fa fa-info', - disabled: (that.sqlAlterHelp == '' && that.sqlCreateHelp == '' && !that.epasHelp) ? true : false, - register: function(btn) { - btn.on('click',() => { - onSqlHelp(); - }); - }, - }); - createButtons(buttons, 'header', 'pg-prop-btn-group-above'); - } - j.append(content); - }.bind(panel), - onSqlHelp = function() { - // Construct the URL - let server = pgBrowser.tree.getTreeNodeHierarchy(item).server; - - let url = pgBrowser.utils.pg_help_path; - let fullUrl = ''; - - if (server.server_type == 'ppas' && that.epasHelp) { - fullUrl = help.getEPASHelpUrl(server.version); - } else { - if (that.sqlCreateHelp == '' && that.sqlAlterHelp != '') { - fullUrl = help.getHelpUrl(url, that.sqlAlterHelp, server.version); - } else if (that.sqlCreateHelp != '' && that.sqlAlterHelp == '') { - fullUrl = help.getHelpUrl(url, that.sqlCreateHelp, server.version); - } else { - if (view.model.isNew()) { - fullUrl = help.getHelpUrl(url, that.sqlCreateHelp, server.version); - } else { - fullUrl = help.getHelpUrl(url, that.sqlAlterHelp, server.version); - } - } - } - - window.open(fullUrl, 'postgres_help'); + let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item); + getNodeView( + that.type, treeNodeInfo, 'properties', data, 'tab', j[0], this, onEdit + ); + return; }.bind(panel), onDialogHelp = function() { @@ -1427,52 +1066,6 @@ define('pgadmin.browser.node', [ } }.bind(panel), - warnBeforeAttributeChange = function(yes_callback) { - let $props = this.$container.find('.obj_properties').first(), - objview = $props && $props.data('obj-view'), - self = this; - - if (objview && objview.model && !_.isUndefined(objview.model.warn_text) && !_.isNull(objview.model.warn_text)) { - let warn_text; - warn_text = gettext(objview.model.warn_text); - if(objview.model.sessChanged()){ - Notify.confirm( - gettext('Warning'), - warn_text, - function() { - setTimeout(function(){ - yes_callback(); - }.bind(self), 50); - return true; - }, - function() { - return true; - } - ); - } else { - return true; - } - } else { - yes_callback(); - return true; - } - }.bind(panel), - - informBeforeAttributeChange = function(ok_callback) { - let $props = this.$container.find('.obj_properties').first(), - objview = $props && $props.data('obj-view'); - - if (objview && objview.model && !_.isUndefined(objview.model.inform_text) && !_.isNull(objview.model.inform_text)) { - Notify.alert( - gettext('Warning'), - gettext(objview.model.inform_text) - ); - - } - ok_callback(); - return true; - }.bind(panel), - onSave = function(_view, saveBtn) { let m = _view.model, d = m.toJSON(true), @@ -1547,229 +1140,41 @@ define('pgadmin.browser.node', [ /* Remove any dom rendered by getNodeView */ removeNodeView(j[0]); /* getSchema is a schema for React. Get the react node view */ - if(that.getSchema) { - let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item); - getNodeView( - that.type, treeNodeInfo, action, data, 'dialog', j[0], this, onEdit, - (nodeData)=>{ - if(nodeData.node) { - onSaveFunc(nodeData.node, treeNodeInfo); - // Removing the node-prop property of panel - // so that we show updated data on panel - let pnlProperties = pgBrowser.docker.findPanels('properties')[0], - pnlSql = pgBrowser.docker.findPanels('sql')[0], - pnlStats = pgBrowser.docker.findPanels('statistics')[0], - pnlDependencies = pgBrowser.docker.findPanels('dependencies')[0], - pnlDependents = pgBrowser.docker.findPanels('dependents')[0]; + let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item); + getNodeView( + that.type, treeNodeInfo, action, data, 'dialog', j[0], this, onEdit, + (nodeData)=>{ + if(nodeData.node) { + onSaveFunc(nodeData.node, treeNodeInfo); + // Removing the node-prop property of panel + // so that we show updated data on panel + let pnlProperties = pgBrowser.docker.findPanels('properties')[0], + pnlSql = pgBrowser.docker.findPanels('sql')[0], + pnlStats = pgBrowser.docker.findPanels('statistics')[0], + pnlDependencies = pgBrowser.docker.findPanels('dependencies')[0], + pnlDependents = pgBrowser.docker.findPanels('dependents')[0]; - if (pnlProperties) - $(pnlProperties).removeData('node-prop'); - if (pnlSql) - $(pnlSql).removeData('node-prop'); - if (pnlStats) - $(pnlStats).removeData('node-prop'); - if (pnlDependencies) - $(pnlDependencies).removeData('node-prop'); - if (pnlDependents) - $(pnlDependents).removeData('node-prop'); + if (pnlProperties) + $(pnlProperties).removeData('node-prop'); + if (pnlSql) + $(pnlSql).removeData('node-prop'); + if (pnlStats) + $(pnlStats).removeData('node-prop'); + if (pnlDependencies) + $(pnlDependencies).removeData('node-prop'); + if (pnlDependents) + $(pnlDependents).removeData('node-prop'); - if(nodeData.success === 0) { - Notify.alert(gettext('Error'), - gettext(nodeData.errormsg) - ); - } + if(nodeData.success === 0) { + Notify.alert(gettext('Error'), + gettext(nodeData.errormsg) + ); } } - ); - return; - } - - // We need to release any existing view, before - // creating the new view. - if (view) { - // Release the view - view.remove({ - data: true, - internal: true, - silent: true, - }); - // Deallocate the view - view = null; - // Reset the data object - j.data('obj-view', null); - } - // Make sure the HTML element is empty. - j.empty(); - - that.header = $('
').addClass( - 'pg-prop-header' - ).appendTo(j); - that.footer = $('
').addClass( - 'pg-prop-footer' - ).appendTo(j); - - let updateButtons = function(hasError, modified) { - - let btnGroup = this.find('.pg-prop-btn-group'), - btnSave = btnGroup.find('button.btn-primary'), - btnReset = btnGroup.find('button.btn-secondary[type="reset"]'); - - if (hasError || !modified) { - btnSave.prop('disabled', true); - btnSave.attr('disabled', 'disabled'); - } else { - btnSave.prop('disabled', false); - btnSave.removeAttr('disabled'); } + ); + return; - if (!modified) { - btnReset.prop('disabled', true); - btnReset.attr('disabled', 'disabled'); - } else { - btnReset.prop('disabled', false); - btnReset.removeAttr('disabled'); - } - }; - - // Create a view to edit/create the properties in fieldsets - view = that.getView(item, action, content, data, 'dialog', updateButtons, j, onCancelFunc); - if (view) { - // Save it to release it later - j.data('obj-view', view); - - self.icon( - _.isFunction(that['node_image']) ? - (that['node_image']).apply(that, [data, view.model]) : - (that['node_image'] || ('icon-' + that.type)) - ); - - // Create proper buttons - let btn_grp = createButtons([{ - label: '', - type: 'help', - tooltip: gettext('SQL help for this object type.'), - extraClasses: ['btn-primary-icon', 'pull-left', 'mx-1'], - icon: 'fa fa-info', - disabled: (that.sqlAlterHelp == '' && that.sqlCreateHelp == '' && !that.epasHelp) ? true : false, - register: function(btn) { - btn.on('click',() => { - onSqlHelp(); - }); - }, - }, { - label: '', - type: 'help', - tooltip: gettext('Help for this dialog.'), - extraClasses: ['btn-primary-icon', 'pull-left', 'mx-1'], - icon: 'fa fa-question', - disabled: (that.dialogHelp == '') ? true : false, - register: function(btn) { - btn.on('click',() => { - onDialogHelp(); - }); - }, - }, { - label: gettext('Cancel'), - type: 'cancel', - tooltip: gettext('Cancel changes to this object.'), - extraClasses: ['btn-secondary', 'mx-1'], - icon: 'fa fa-times', - disabled: false, - register: function(btn) { - btn.on('click',() => { - // Removing the action-mode - self.$container.removeAttr('action-mode'); - onCancelFunc.call(true); - }); - }, - }, { - label: gettext('Reset'), - type: 'reset', - tooltip: gettext('Reset the fields on this dialog.'), - extraClasses: ['btn-secondary', 'mx-1'], - icon: 'fa fa-recycle', - disabled: true, - register: function(btn) { - btn.on('click',() => { - warnBeforeChangesLost.call( - self, - gettext('Changes will be lost. Are you sure you want to reset?'), - function() { - setTimeout(function() { - editFunc.call(); - }, 0); - } - ); - }); - }, - }, { - label: gettext('Save'), - type: 'save', - tooltip: gettext('Save this object.'), - extraClasses: ['btn-primary', 'mx-1'], - icon: 'fa fa-save', - disabled: true, - register: function(btn) { - // Save the changes - btn.on('click',() => { - warnBeforeAttributeChange.call( - self, - function() { - informBeforeAttributeChange.call(self, function(){ - setTimeout(function() { - onSave.call(this, view, btn); - }, 0); - }); - } - ); - }); - }, - }], 'footer', 'pg-prop-btn-group-below'); - - btn_grp.on('keydown', 'button', function(event) { - if (!event.shiftKey && event.keyCode == 9 && $(this).nextAll('button:not([disabled])').length == 0) { - // set focus back to first focusable element on dialog - view.$el.closest('.wcFloating').find('[tabindex]:not([tabindex="-1"]').first().focus(); - return false; - } - let btnGroup = $(self.$container.find('.pg-prop-btn-group')); - let el = $(btnGroup).find('button:first'); - if (self.$container.find('.number-cell.editable:last').is(':visible')){ - if (event.keyCode === 9 && event.shiftKey) { - if ($(el).is($(event.target))){ - $(self.$container.find('td.editable:last').trigger('click')); - event.preventDefault(); - event.stopPropagation(); - } - } - } - }); - - setTimeout(function() { - pgBrowser.keyboardNavigation.getDialogTabNavigator(self.pgElContainer); - }, 200); - } - - // Create status bar. - createStatusBar('footer'); - - // Add some space, so that - button group does not override the - // space - content.addClass('pg-prop-has-btn-group-below'); - - // Show contents before buttons - j.prepend(content); - // add required attributes to select2 input to resolve accessibility issue. - $('.select2-search__field').attr('aria-label', 'select2'); - view.$el.closest('.wcFloating').find('.wcFrameButtonBar > .wcFrameButton[style!="display: none;"]').on('keydown', function(e) { - - if(e.shiftKey && e.keyCode === 9) { - e.stopPropagation(); - setTimeout(() => { - view.$el.closest('.wcFloating').find('[tabindex]:not([tabindex="-1"]):not([disabled])').last().focus(); - }, 10); - } - }); }.bind(panel), closePanel = function(confirm_close_flag) { if(!_.isUndefined(confirm_close_flag)) { @@ -1832,7 +1237,6 @@ define('pgadmin.browser.node', [ }]); }, 0); }, - onCancelFunc = closePanel, onSaveFunc = updateTreeItem.bind(panel, that), onEdit = editFunc.bind(panel); diff --git a/web/pgadmin/browser/static/js/node.ui.js b/web/pgadmin/browser/static/js/node.ui.js deleted file mode 100644 index 48c56f0de..000000000 --- a/web/pgadmin/browser/static/js/node.ui.js +++ /dev/null @@ -1,564 +0,0 @@ -///////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2022, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -////////////////////////////////////////////////////////////// -import _ from 'lodash'; - -define([ - 'sources/gettext', 'jquery', 'sources/pgadmin', 'backform', - 'backgrid', 'select2', 'pgadmin.browser.node', -], function(gettext, $, pgAdmin, Backform, Backgrid) { - - /* - * Define the selectAll adapter for select2. - * - * Reference: - * https://github.com/select2/select2/issues/195#issuecomment-240130634 - */ - $.fn.select2.amd.define('select2/selectAllAdapter', [ - 'select2/utils', - 'select2/dropdown', - 'select2/dropdown/attachBody', - ], function(Utils, Dropdown, AttachBody) { - - function SelectAll() {/*This is intentional (SonarQube)*/} - SelectAll.prototype.render = function(decorated) { - let self = this; - let $rendered = decorated.call(this); - - let $selectAll = $([ - '', - ].join('')); - - let $unselectAll = $([ - '', - ].join('')); - - let $btnContainer = $( - '
' - ).append($selectAll).append($unselectAll); - - if (!this.$element.prop('multiple')) { - // this isn't a multi-select -> don't add the buttons! - return $rendered; - } - $rendered.find('.select2-dropdown').prepend($btnContainer); - // Select All button click - $selectAll.on('click', function() { - $rendered.find('.select2-results__option[aria-selected=false]').each( - function() { - // Note: With latest version we do not get data in the data attribute of a element - // Hence as per new logic we will fetch the data from the cache created by Select2. - let data = Utils.GetData($(this)[0], 'data'); - self.trigger('select', { - data: data, - }); - } - ); - self.trigger('close'); - }); - // Unselect All button click - $unselectAll.on('click', function() { - $rendered.find('.select2-results__option[aria-selected=true]').each( - function() { - let data = Utils.GetData($(this)[0], 'data'); - self.trigger('unselect', { - data: data, - }); - } - ); - self.trigger('close'); - }); - return $rendered; - }; - - return Utils.Decorate( - Utils.Decorate( - Dropdown, - AttachBody - ), - SelectAll - ); - }); - - /* - * NodeAjaxOptionsControl - * This control will fetch the options required to render the select - * control, from the url specific to the pgAdmin.Browser node object. - * - * In order to use this properly, schema require to set the 'url' property, - * which exposes the data for this node. - * - * In case the url is not providing the data in proper format, we can - * specify the 'transform' function too, which will convert the fetched - * data to proper 'label', 'value' format. - */ - let NodeAjaxOptionsControl = Backform.NodeAjaxOptionsControl = - Backform.Select2Control.extend({ - defaults: _.extend(Backform.Select2Control.prototype.defaults, { - url: undefined, - transform: undefined, - url_with_id: false, - select2: { - allowClear: true, - placeholder: gettext('Select an item...'), - width: 'style', - }, - }), - initialize: function() { - /* - * Initialization from the original control. - */ - Backform.Select2Control.prototype.initialize.apply(this, arguments); - - /* - * We're about to fetch the options required for this control. - */ - let self = this, - url = self.field.get('url') || self.defaults.url, - m = self.model.top || self.model, - url_jump_after_node = self.field.get('url_jump_after_node') || null; - - // Hmm - we found the url option. - // That means - we needs to fetch the options from that node. - if (url) { - let node = this.field.get('schema_node'), - node_info = this.field.get('node_info'), - with_id = this.field.get('url_with_id') || false, - full_url = node.generate_url.apply( - node, [ - null, url, this.field.get('node_data'), with_id, node_info, url_jump_after_node, - ]), - cache_level, - cache_node = this.field.get('cache_node'); - - cache_node = (cache_node && pgAdmin.Browser.Nodes[cache_node]) || node; - - if (this.field.has('cache_level')) { - cache_level = this.field.get('cache_level'); - } else { - cache_level = cache_node.cache_level(node_info, with_id); - } - - /* - * We needs to check, if we have already cached data for this url. - * If yes - use that, and do not bother about fetching it again, - * and use it. - */ - let data = cache_node.cache(node.type + '#' + url, node_info, cache_level); - - if (this.field.get('version_compatible') && - (_.isUndefined(data) || _.isNull(data))) { - m.trigger('pgadmin:view:fetching', m, self.field); - $.ajax({ - async: false, - url: full_url, - }) - .done(function(res) { - /* - * We will cache this data for short period of time for avoiding - * same calls. - */ - data = cache_node.cache(node.type + '#' + url, node_info, cache_level, res.data); - }) - .fail(function() { - m.trigger('pgadmin:view:fetch:error', m, self.field); - }); - m.trigger('pgadmin:view:fetched', m, self.field); - } - // To fetch only options from cache, we do not need time from 'at' - // attribute but only options. - // - // It is feasible that the data may not have been fetched. - data = (data && data.data) || []; - - /* - * Transform the data - */ - let transform = this.field.get('transform') || self.defaults.transform; - if (transform && _.isFunction(transform)) { - // We will transform the data later, when rendering. - // It will allow us to generate different data based on the - // dependencies. - self.field.set('options', transform.bind(self, data)); - } else { - self.field.set('options', data); - } - } - }, - }); - - let formatNode = function(opt) { - if (!opt.id) { - return opt.text; - } - - let optimage = $(opt.element).data('image'); - - if (!optimage) { - return opt.text; - } else { - return $('').append( - $('', { - class: 'wcTabIcon ' + optimage, - }) - ).append($('').text(opt.text)); - } - }; - - let filterRows = function(self, filter, rows, node) { - let res = []; - _.each(rows, function(r) { - if (filter(r)) { - let l = (_.isFunction(node['node_label']) ? - (node['node_label']).apply(node, [r, self.model, self]) : - r.label), - image = (_.isFunction(node['node_image']) ? - (node['node_image']).apply( - node, [r, self.model, self] - ) : - (node['node_image'] || ('icon-' + node.type))); - - res.push({ - 'value': r._id, - 'image': image, - 'label': l, - }); - } - }); - - return res; - }; - - let NodeListByIdControl = Backform.NodeListByIdControl = NodeAjaxOptionsControl.extend({ - controlClassName: 'pgadmin-node-select form-control', - defaults: _.extend({}, NodeAjaxOptionsControl.prototype.defaults, { - url: 'nodes', - filter: undefined, - transform: function(rows) { - let self = this, - node = self.field.get('schema_node'), - filter = self.field.get('filter') || function() { - return true; - }; - - filter = filter.bind(self); - return filterRows(self, filter, rows, node); - }, - select2: { - allowClear: true, - placeholder: gettext('Select an item...'), - width: 'style', - templateResult: formatNode, - templateSelection: formatNode, - }, - }), - }); - - Backform.NodeListByNameControl = NodeListByIdControl.extend({ - defaults: _.extend({}, NodeListByIdControl.prototype.defaults, { - transform: function(rows) { - let self = this, - node = self.field.get('schema_node'), - res = [], - filter = self.field.get('filter') || function() { - return true; - }; - - filter = filter.bind(self); - - _.each(rows, function(r) { - if (filter(r)) { - let l = (_.isFunction(node['node_label']) ? - (node['node_label']).apply(node, [r, self.model, self]) : - r.label), - image = (_.isFunction(node['node_image']) ? - (node['node_image']).apply( - node, [r, self.model, self] - ) : - (node['node_image'] || ('icon-' + node.type))); - res.push({ - 'value': r.label, - 'image': image, - 'label': l, - }); - } - }); - - return res; - }, - }), - }); - - /* - * Global function to make visible particular dom element in it's parent - * with given class. - */ - $.fn.pgMakeVisible = function(cls) { - return this.each(function() { - if (!this || !$(this.length)) - return; - let top, p = $(this), - hasScrollbar = function(j) { - if (j && j.length > 0) { - return j.get(0).scrollHeight > j.height(); - } - return false; - }; - - // check if p is not empty - while (p && p.length > 0) { - top = p.get(0).offsetTop + p.height(); - p = p.parent(); - if (hasScrollbar(p)) { - p.scrollTop(top); - } - if (p.hasClass(cls)) //'backform-tab' - return; - } - }); - }; - - /* - * Global function to make visible backgrid new row - */ - $.fn.pgMakeBackgridVisible = function(cls) { - return this.each(function() { - if (!this || !$(this.length)) - return; - - let elem = $(this), - backgridDiv = $(this).offsetParent().parent(), // Backgrid div.subnode - backgridDivTop = backgridDiv.offset().top, - backgridDivHeight = backgridDiv.height(), - backformTab = $(this).closest(cls), // Backform-tab - gridScroll = null; - - if(backformTab.length == 0) { - return false; - } - gridScroll = backformTab[0].offsetHeight - backgridDivTop; - - if (backgridDivHeight > gridScroll) { - let top = elem.get(0).offsetTop + elem.height(); - backformTab.find('.tab-content').scrollTop(top); - } - return true; - }); - }; - - /* - * NodeAjaxOptionsCell - * This cell will fetch the options required to render the select - * cell, from the url specific to the pgAdmin.Browser node object. - * - * In order to use this properly, schema require to set the 'url' property, - * which exposes the data for this node. - * - * In case the url is not providing the data in proper format, we can - * specify the 'transform' function too, which will convert the fetched - * data to proper 'label', 'value' format. - */ - let NodeAjaxOptionsCell = Backgrid.Extension.NodeAjaxOptionsCell = Backgrid.Extension.Select2Cell.extend({ - defaults: _.extend({}, Backgrid.Extension.Select2Cell.prototype.defaults, { - url: undefined, - transform: undefined, - url_with_id: false, - select2: { - allowClear: true, - placeholder: gettext('Select an item...'), - width: 'style', - }, - opt: { - label: null, - value: null, - image: null, - selected: false, - }, - }), - template: _.template( - '' - ), - initialize: function() { - Backgrid.Extension.Select2Cell.prototype.initialize.apply(this, arguments); - - let url = this.column.get('url') || this.defaults.url, - is_options_cached = _.has(this.column.attributes, 'options_cached'), - options_cached = is_options_cached && this.column.get('options_cached'); - // Hmm - we found the url option. - // That means - we needs to fetch the options from that node. - if (url && !options_cached) { - - let self = this, - m = this.model, - column = this.column, - eventHandler = m.top || m, - node = column.get('schema_node'), - node_info = column.get('node_info'), - with_id = column.get('url_with_id') || false, - url_jump_after_node = this.column.get('url_jump_after_node') || null, - full_url = node.generate_url.apply( - node, [ - null, url, column.get('node_data'), with_id, node_info, url_jump_after_node, - ]), - cache_level, - cache_node = column.get('cache_node'); - - cache_node = (cache_node && pgAdmin.Browser.Nodes[cache_node]) || node; - - if (column.has('cache_level')) { - cache_level = column.get('cache_level'); - } else { - cache_level = cache_node.cache_level(node_info, with_id); - } - - /* - * We needs to check, if we have already cached data for this url. - * If yes - use that, and do not bother about fetching it again, - * and use it. - */ - let data = cache_node.cache(node.type + '#' + url, node_info, cache_level); - - if (column.get('version_compatible') && - (_.isUndefined(data) || _.isNull(data))) { - eventHandler.trigger('pgadmin:view:fetching', m, column); - $.ajax({ - async: false, - url: full_url, - }) - .done(function(res) { - /* - * We will cache this data for short period of time for avoiding - * same calls. - */ - data = cache_node.cache(node.type + '#' + url, node_info, cache_level, res.data); - }) - .fail(function() { - eventHandler.trigger('pgadmin:view:fetch:error', m, column); - }); - eventHandler.trigger('pgadmin:view:fetched', m, column); - } - // To fetch only options from cache, we do not need time from 'at' - // attribute but only options. - // - // It is feasible that the data may not have been fetched. - data = (data && data.data) || []; - - /* - * Transform the data - */ - let transform = column.get('transform') || self.defaults.transform; - if (transform && _.isFunction(transform)) { - // We will transform the data later, when rendering. - // It will allow us to generate different data based on the - // dependencies. - column.set('options', transform.bind(column, data)); - } else { - column.set('options', data); - } - - if (is_options_cached) { - column.set('options_cached', true); - } - } - }, - }); - - let transformFunc = function(rows, control) { - let self = control || this, - node = self.column.get('schema_node'), - filter = self.column.get('filter') || function() { - return true; - }; - - filter = filter.bind(self); - return filterRows(self, filter, rows, node); - }; - - Backgrid.Extension.NodeListByIdCell = NodeAjaxOptionsCell.extend({ - controlClassName: 'pgadmin-node-select backgrid-cell', - defaults: _.extend({}, NodeAjaxOptionsCell.prototype.defaults, { - url: 'nodes', - filter: undefined, - transform: transformFunc, - select2: { - placeholder: gettext('Select an item...'), - width: 'style', - templateResult: formatNode, - templateSelection: formatNode, - }, - }), - }); - - Backgrid.Extension.NodeListByNameCell = NodeAjaxOptionsCell.extend({ - controlClassName: 'pgadmin-node-select backgrid-cell', - defaults: _.extend({}, NodeAjaxOptionsCell.prototype.defaults, { - url: 'nodes', - filter: undefined, - transform: transformFunc, - select2: { - placeholder: gettext('Select an item...'), - width: 'style', - templateResult: formatNode, - templateSelection: formatNode, - }, - }), - }); - - // Extend the browser's node model class to create a option/value pair - Backgrid.Extension.MultiSelectAjaxCell = Backgrid.Extension.NodeAjaxOptionsCell.extend({ - defaults: _.extend({}, NodeAjaxOptionsCell.prototype.defaults, { - transform: undefined, - url_with_id: false, - select2: { - allowClear: true, - placeholder: gettext('Select an item...'), - width: 'style', - multiple: true, - }, - opt: { - label: null, - value: null, - image: null, - selected: false, - }, - }), - getValueFromDOM: function() { - let res = []; - - this.$el.find('select').find(':selected').each(function() { - res.push($(this).attr('value')); - }); - - return res; - }, - }); - - /* - * Control to select multiple columns. - */ - Backform.MultiSelectAjaxControl = NodeAjaxOptionsControl.extend({ - defaults: _.extend({}, NodeAjaxOptionsControl.prototype.defaults, { - select2: { - multiple: true, - allowClear: true, - width: 'style', - }, - }), - }); - - return Backform; -}); diff --git a/web/pgadmin/dashboard/static/css/dashboard.css b/web/pgadmin/dashboard/static/css/dashboard.css index d951fca97..7a42dcd0d 100644 --- a/web/pgadmin/dashboard/static/css/dashboard.css +++ b/web/pgadmin/dashboard/static/css/dashboard.css @@ -30,7 +30,7 @@ display: block; } -/* CSS to make subnode control look preety in backgrid - START */ +/* CSS to make subnode control look pretty - START */ .dashboard-tab-container .subnode-dialog .form-control { font-size: inherit; } diff --git a/web/pgadmin/settings/static/js/settings.js b/web/pgadmin/settings/static/js/settings.js index 6bfbc12d6..6beff2b9b 100644 --- a/web/pgadmin/settings/static/js/settings.js +++ b/web/pgadmin/settings/static/js/settings.js @@ -11,7 +11,7 @@ import Notify from '../../../static/js/helpers/Notifier'; define('pgadmin.settings', [ 'jquery', 'sources/pgadmin', - 'sources/gettext', 'sources/url_for', 'pgadmin.backform', + 'sources/gettext', 'sources/url_for', ], function($, pgAdmin, gettext, url_for) { // This defines the Preference/Options Dialog for pgAdmin IV. diff --git a/web/pgadmin/static/css/style.css b/web/pgadmin/static/css/style.css index 62c52adac..db1bf87f1 100644 --- a/web/pgadmin/static/css/style.css +++ b/web/pgadmin/static/css/style.css @@ -2,7 +2,6 @@ @import 'node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker3.css'; @import 'node_modules/tempusdominus-bootstrap-4/build/css/tempusdominus-bootstrap-4.css'; @import 'node_modules/bootstrap4-toggle/css/bootstrap4-toggle.min.css'; -@import 'node_modules/backgrid-filter/backgrid-filter.css'; @import 'node_modules/jquery-contextmenu/dist/jquery.contextMenu.css'; @import 'node_modules/webcabin-docker/Build/wcDocker.css'; @import 'node_modules/select2/dist/css/select2.min.css'; @@ -12,9 +11,6 @@ @import 'node_modules/codemirror/addon/dialog/dialog.css'; @import 'node_modules/codemirror/addon/scroll/simplescrollbars.css'; -@import '../vendor/backgrid/backgrid.css'; -@import '../vendor/backgrid/backgrid-select-all.css'; - @import 'node_modules/xterm/css/xterm.css'; @import 'node_modules/jsoneditor/dist/jsoneditor.min.css'; diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js deleted file mode 100644 index df5019218..000000000 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ /dev/null @@ -1,3558 +0,0 @@ -///////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2022, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -////////////////////////////////////////////////////////////// - -import { calcFontSize } from './utils'; - -define([ - 'sources/gettext', 'underscore', 'jquery', - 'backbone', 'backform', 'backgrid', 'codemirror', - 'sources/keyboard_shortcuts', 'sources/window', 'sources/select2/configure_show_on_scroll', - 'color-picker', 'pgadmin.backgrid', 'select2', 'bootstrap.toggle', -], function(gettext, _, $, Backbone, Backform, Backgrid, CodeMirror, - keyboardShortcuts, pgWindow, configure_show_on_scroll, - Pickr) { - - let pgAdmin = (window.pgAdmin = window.pgAdmin || {}), - pgBrowser = pgAdmin.Browser; - - pgAdmin.editableCell = function() { - if (this.attributes && !_.isUndefined(this.attributes.disabled) && - !_.isNull(this.attributes.disabled)) { - if (_.isFunction(this.attributes.disabled)) { - return !(this.attributes.disabled.apply(this, arguments)); - } - if (_.isBoolean(this.attributes.disabled)) { - return !this.attributes.disabled; - } - } - }; - - // HTML markup global class names. More can be added by individual controls - // using _.extend. Look at RadioControl as an example. - _.extend(Backform, { - controlLabelClassName: 'control-label pg-el-sm-3 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-9 pg-el-12', - controlContainerClassName: 'pgadmin-controls pg-el-sm-9 pg-el-12', - groupClassName: 'pgadmin-control-group form-group row pg-el-12', - setGroupClassName: 'set-group pg-el-12', - tabClassName: 'backform-tab pg-el-12', - setGroupContentClassName: 'fieldset-content pg-el-12', - accordianGroupClassName: 'accordian-group pg-el-12', - accordianContentClassName: 'accordian-content pg-el-12', - hiddenClassName: 'd-none', - helpMessageClassName: 'form-text text-muted help-block', - helpBlockControlClass: 'pgadmin-controls offset-sm-3 pg-el-sm-9 pg-el-12', - }); - - Backform.controlMapper = { - 'int': ['uneditable-input', 'numeric', 'integer'], - 'text': ['uneditable-input', 'input', 'string'], - 'numeric': ['uneditable-input', 'numeric', 'numeric'], - 'date': 'datepicker', - 'datetime': 'datetimepicker', - 'boolean': 'boolean', - 'options': ['readonly-option', 'select', Backgrid.Extension.PGSelectCell], - 'multiline': ['textarea', 'textarea', 'string'], - 'collection': ['sub-node-collection', 'sub-node-collection', 'string'], - 'uniqueColCollection': ['unique-col-collection', 'unique-col-collection', 'string'], - 'switch': 'switch', - 'select2': 'select2', - 'note': 'note', - 'color': 'color', - 'radioModern': 'radioModern', - }; - - Backform.getMappedControl = function(type, mode) { - if (type in Backform.controlMapper) { - let m = Backform.controlMapper[type]; - - if (!_.isArray(m)) { - return m; - } - - let idx, len = _.size(m); - - switch (mode) { - case 'properties': - idx = 0; - break; - case 'edit': - case 'create': - case 'control': - idx = 1; - break; - case 'cell': - idx = 2; - break; - default: - idx = 0; - break; - } - - return m[idx > len ? 0 : idx]; - } - return type; - }; - - /* Returns raw data as it is */ - let RawFormatter = Backform.RawFormatter = function() {/*This is intentional (SonarQube)*/}; - _.extend(RawFormatter.prototype, { - fromRaw: function(rawData) { - return rawData; - }, - toRaw: function(formattedData) { - return formattedData; - }, - }); - - - let BackformControlInit = Backform.Control.prototype.initialize, - BackformControlRemove = Backform.Control.prototype.remove; - - // Override the Backform.Control to allow to track changes in dependencies, - // and rerender the View element - _.extend(Backform.Control.prototype, { - - defaults: _.extend(Backform.Control.prototype.defaults, { - helpMessage: null, - }), - - initialize: function() { - BackformControlInit.apply(this, arguments); - - // Listen to the dependent fields in the model for any change - let deps = this.field.get('deps'); - let self = this; - - if (deps && _.isArray(deps)) { - _.each(deps, function(d) { - let attrArr = d.split('.'); - let name = attrArr.shift(); - self.listenTo(self.model, 'change:' + name, self.render); - }); - } - }, - - remove: function() { - // Remove the events for the dependent fields in the model - let self = this, - deps = self.field.get('deps'); - let attrArr = this.field.get('name').split('.'); - let name = attrArr.shift(); - - self.stopListening(self.model, 'change:' + name, self.render); - if (self.model.errorModel instanceof Backbone.Model) { - self.stopListening( - self.model.errorModel, 'change:' + name, self.updateInvalid - ); - } - - if (deps && _.isArray(deps)) { - _.each(deps, function(d) { - - let attrArray = d.split('.'); - let depname = attrArray.shift(); - - self.stopListening(self.model, 'change:' + depname, self.render); - }); - } - - if (this.cleanup) { - this.cleanup.apply(this); - } - - if (BackformControlRemove) { - BackformControlRemove.apply(self, arguments); - } else { - Backbone.View.prototype.remove.apply(self, arguments); - } - }, - - template: _.template([ - '', - '
', - ' <%=readonly ? "readonly aria-readonly=true" : ""%> id="<%=cId%>" value="<%-value%>" />', - ' <% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - ' <% } %>', - '
', - ].join('\n')), - - clearInvalid: function() { - this.$el.removeClass(Backform.errorClassName); - this.$el.find('.pgadmin-control-error-message').remove(); - return this; - }, - - updateInvalid: function() { - let self = this, - errorModel = this.model.errorModel; - - if (!(errorModel instanceof Backbone.Model)) return this; - - this.clearInvalid(); - - /* - * Find input which have name attribute. - */ - this.$el.find(':input[name]').not('button').each(function(ix, el) { - let error = self.keyPathAccessor( - errorModel.toJSON(), $(el).attr('name') - ); - - if (_.isEmpty(error)) return; - self.$el.addClass(Backform.errorClassName); - }); - }, - - /* - * Overriding the render function of the control to allow us to eval the - * values properly. - */ - render: function() { - let field = _.defaults(this.field.toJSON(), this.defaults), - attributes = this.model.toJSON(), - attrArr = field.name.split('.'), - name = attrArr.shift(), - path = attrArr.join('.'), - rawValue = this.keyPathAccessor(attributes[name], path), - data = _.extend(field, { - rawValue: rawValue, - value: this.formatter.fromRaw(rawValue, this.model), - attributes: attributes, - formatter: this.formatter, - }), - evalF = function(f, d, m) { - return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f); - }; - - // Evaluate the disabled, visible, and required option - _.extend(data, { - disabled: evalF(data.disabled, data, this.model), - readonly: evalF(data.readonly, data, this.model), - visible: evalF(data.visible, data, this.model), - required: evalF(data.required, data, this.model), - }); - - data.cId = data.cId || _.uniqueId('pgC_'); - // Clean up first - this.$el.removeClass(Backform.hiddenClassName); - - if (!data.visible) - this.$el.addClass(Backform.hiddenClassName); - - this.$el.html(this.template(data)).addClass(field.name); - this.updateInvalid(); - - return this; - }, - }); - - /* - * Override the input control events in order to reslove the issue related to - * not updating the value sometimes in the input control. - */ - _.extend( - Backform.InputControl.prototype, { - defaults: _.extend(Backform.InputControl.prototype.defaults, { - controlLabelClassName: Backform.controlLabelClassName, - controlsClassName: Backform.controlsClassName, - }), - template: _.template([ - '', - '
', - ' <%=readonly ? "readonly aria-readonly=true" : ""%> <%=required ? "required" : ""%> />', - ' <% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - ' <% } %>', - '
', - ].join('\n')), - events: { - 'change input': 'onChange', - 'blur input': 'onChange', - 'keyup input': 'onKeyUp', - 'focus input': 'clearInvalid', - }, - onKeyUp: function(ev) { - if (this.key_timeout) { - clearTimeout(this.key_timeout); - } - - this.keyup_timeout = setTimeout(function() { - this.onChange(ev); - }.bind(this), 400); - }, - }); - - /* - * Override the textarea control events in order to resolve the issue related - * to not updating the value in model on certain browsers in few situations - * like copy/paste, deletion using backspace. - * - * Reference: - * http://stackoverflow.com/questions/11338592/how-can-i-bind-to-the-change-event-of-a-textarea-in-jquery - */ - _.extend( - Backform.TextareaControl.prototype, { - defaults: _.extend( - Backform.TextareaControl.prototype.defaults, { - rows: 5, - helpMessage: null, - maxlength: null, - } - ), - events: { - 'change textarea': 'onChange', - 'keyup textarea': 'onKeyUp', - 'paste textarea': 'onChange', - 'selectionchange textarea': 'onChange', - 'focus textarea': 'clearInvalid', - }, - template: _.template([ - '', - '
', - ' ', - ' <% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - ' <% } %>', - '
', - ].join('\n')), - onKeyUp: function(ev) { - if (this.key_timeout) { - clearTimeout(this.key_timeout); - } - - this.keyup_timeout = setTimeout(function() { - this.onChange(ev); - }.bind(this), 400); - }, - }); - - /* - * Overriding the render function of the select control to allow us to use - * options as function, which should return array in the format of - * (label, value) pair. - */ - Backform.SelectControl.prototype.render = function() { - let field = _.defaults(this.field.toJSON(), this.defaults), - attributes = this.model.toJSON(), - attrArr = field.name.split('.'), - name = attrArr.shift(), - path = attrArr.join('.'), - rawValue = this.keyPathAccessor(attributes[name], path), - data = _.extend(field, { - rawValue: rawValue, - value: this.formatter.fromRaw(rawValue, this.model), - attributes: attributes, - formatter: this.formatter, - }), - evalF = function(f, d, m) { - return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f); - }; - - // Evaluate the disabled, visible, and required option - _.extend(data, { - disabled: evalF(data.disabled, data, this.model), - visible: evalF(data.visible, data, this.model), - required: evalF(data.required, data, this.model), - }); - // Evaluation the options - if (_.isFunction(data.options)) { - try { - data.options = data.options(this); - } catch (e) { - // Do nothing - data.options = []; - this.model.trigger('pgadmin-view:transform:error', this.model, this.field, e); - } - } - - data.cId = data.cId || _.uniqueId('pgC_'); - // Clean up first - this.$el.removeClass(Backform.hiddenClassName); - - if (!data.visible) - this.$el.addClass(Backform.hiddenClassName); - - this.$el.html(this.template(data)).addClass(field.name); - this.updateInvalid(); - - return this; - }; - - Backform.SelectControl.prototype.template = _.template([ - '', - '
', - ' ', - '
', - ].join('\n')); - - _.extend(Backform.SelectControl.prototype.defaults, { - helpMessage: null, - }); - - Backform.ReadonlyOptionControl = Backform.SelectControl.extend({ - template: _.template([ - '', - '
', - '<% for (let i=0; i < options.length; i++) { %>', - ' <% let option = options[i]; %>', - ' <% if (option.value === rawValue) { %>', - ' ', - ' <% } %>', - '<% } %>', - '<% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - '<% } %>', - '
', - ].join('\n')), - events: {}, - getValueFromDOM: function() { - return this.formatter.toRaw(this.$el.find('span').text(), this.model); - }, - }); - - /* - * Override the function 'updateInvalid' of the radio control to resolve an - * issue, which will not render the error block multiple times for each - * options. - */ - _.extend( - Backform.RadioControl.prototype, { - updateInvalid: function() { - let self = this, - errorModel = this.model.errorModel; - - if (!(errorModel instanceof Backbone.Model)) return this; - - this.clearInvalid(); - - /* - * Find input which have name attribute. - */ - this.$el.find(':input[name]').not('button').each(function(ix, el) { - let error = self.keyPathAccessor( - errorModel.toJSON(), $(el).attr('name') - ); - - if (_.isEmpty(error)) return; - - self.$el.addClass(Backform.errorClassName).find( - '[type="radio"]' - ).append( - $('
').addClass( - 'pgadmin-control-error-message pg-el-offset-4 pg-el-8 pg-el-8 help-block' - ).text(error)); - }); - }, - }); - - - Backform.RadioModernControl = Backform.RadioControl.extend({ - defaults: { - controlLabelClassName: Backform.controlLabelClassName, - controlsClassName: Backform.controlsClassName, - extraClasses: [], - helpMessage: '', - name: '', - }, - events: _.extend({}, Backform.InputControl.prototype.events, { - 'click label.btn': 'toggle', - }), - toggle: function(e) { - /* Toggle the other buttons to unchecked and current to checked */ - let $curr = $(e.currentTarget), - $btn_group = $curr.closest('.btn-group'); - - $btn_group.find('.btn') - .removeClass('btn-primary') - .addClass('btn-secondary') - .find('.fa') - .addClass('visibility-hidden') - .siblings('input') - .prop('checked', false); - - $curr.removeClass('btn-secondary') - .addClass('btn-primary') - .find('.fa') - .removeClass('visibility-hidden') - .siblings('input') - .prop('checked', true) - .trigger('change'); - - e.preventDefault(); - e.stopPropagation(); - }, - template: _.template([ - '<% if (label) { %>', - '', - '<% } %>', - '
', - '
aria-labelledby="<%=cId%>_grplabel" <%}%>>', - ' <% for (let i=0; i < options.length; i++) { %>', - ' <% let option = options[i]; %>', - ' ', - ' <% } %>', - '
', - ' <% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - ' <% } %>', - '
', - ].join('\n')), - formatter: RawFormatter, - getValueFromDOM: function() { - return this.formatter.toRaw(this.$el.find('input[type="radio"]:checked').attr('value'), this.model); - }, - render: function() { - let field = _.defaults(this.field.toJSON(), this.defaults), - attributes = this.model.toJSON(), - attrArr = field.name.split('.'), - name = attrArr.shift(), - path = attrArr.join('.'), - rawValue = this.keyPathAccessor(attributes[name], path), - data = _.extend(field, { - rawValue: rawValue, - value: this.formatter.fromRaw(rawValue, this.model), - attributes: attributes, - formatter: this.formatter, - }), - // Evaluate the disabled, visible, and required option - evalF = function evalF(f, d, m) { - return _.isFunction(f) ? !!f.apply(d, [m]) : !!f; - }; - - _.extend(data, { - disabled: evalF(data.disabled, data, this.model), - visible: evalF(data.visible, data, this.model), - required: evalF(data.required, data, this.model), - }); // Clean up first - - data.cId = data.cId || _.uniqueId('pgC_'); - data.options = _.isFunction(data.options) ? - data.options.apply(data, [this.model]) : data.options; - - this.$el.removeClass(Backform.hiddenClassName); - if (!data.visible) this.$el.addClass(Backform.hiddenClassName); - this.$el.html(this.template(data)).addClass(field.name); - this.updateInvalid(); - - this.$el.find('.btn').on('keyup', (e)=>{ - /* Spacebar click */ - if (e.keyCode == 32) { - $(e.currentTarget).trigger('click'); - } - }); - return this; - }, - }); - - // Requires the Bootstrap Switch to work. - Backform.SwitchControl = Backform.InputControl.extend({ - defaults: { - label: '', - options: { - onText: gettext('Yes'), - offText: gettext('No'), - onColor: 'success', - offColor: 'ternary', - size: 'mini', - width: null, - height: null, - }, - controlLabelClassName: Backform.controlLabelClassName, - controlsClassName: Backform.controlsClassName, - extraClasses: [], - helpMessage: null, - extraToggleClasses: null, - }, - template: _.template([ - '<%=label%>', - '
', - ' <%=disabled ? "disabled" : ""%> <%=readonly ? "disabled" : ""%> <%=required ? "required" : ""%> />', - ' <% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - ' <% } %>', - '
', - ].join('\n')), - getValueFromDOM: function() { - return this.formatter.toRaw( - this.$input.prop('checked'), - this.model - ); - }, - events: { - 'change input': 'onChange', - 'keyup': 'toggleSwitch', - }, - setSrValue: function() { - let {onText, offText} = _.defaults(this.field.get('options'), this.defaults.options); - let label = this.field.get('label'); - - if(this.$el.find('.toggle.btn').hasClass('off')) { - this.$el.find('.sr-value').text(` - ${label}, ${offText}, ` + gettext('Toggle') + ` - `); - } else { - this.$el.find('.sr-value').text(` - ${label}, ${onText}, ` + gettext('Toggle') + ` - `); - } - }, - onChange: function() { - Backform.InputControl.prototype.onChange.apply(this, arguments); - this.setSrValue(); - }, - toggleSwitch: function(e) { - if (e.keyCode == 32) { - this.$el.find('input[type=checkbox]').bootstrapToggle('toggle'); - e.preventDefault(); - this.setSrValue(); - } - }, - render: function() { - let field = _.defaults(this.field.toJSON(), this.defaults), - attributes = this.model.toJSON(), - attrArr = field.name.split('.'), - name = attrArr.shift(), - path = attrArr.join('.'), - rawValue = this.keyPathAccessor(attributes[name], path), - data = _.extend(field, { - rawValue: rawValue, - value: this.formatter.fromRaw(rawValue, this.model), - attributes: attributes, - formatter: this.formatter, - }), - evalF = function(f, d, m) { - return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f); - }; - - // Evaluate the disabled, visible, and required option - _.extend(data, { - disabled: evalF(field.disabled, field, this.model), - readonly: evalF(field.readonly, field, this.model), - visible: evalF(data.visible, field, this.model), - required: evalF(data.required, field, this.model), - }); - - data.cId = data.cId || _.uniqueId('pgC_'); - // Clean up first - this.$el.removeClass(Backform.hiddenClassName); - - if (!data.visible) - this.$el.addClass(Backform.hiddenClassName); - - if(Backform.requiredInputClassName) { - this.$el.removeClass(Backform.requiredInputClassName); - } - - if (data.required) { - this.$el.addClass(Backform.requiredInputClassName); - } - - /* Set disabled for both disabled and readonly */ - data.options = _.defaults({ - disabled: data.disabled || data.readonly, - }, this.field.get('options'), this.defaults.options, - $.fn.bootstrapToggle.defaults); - - this.$el.html(this.template(data)).addClass(field.name); - - // Add Extra Toggle classes to render multiple toggles in a single row - if(!_.isNull(field.extraToggleClasses)) this.$el.addClass(field.extraToggleClasses); - - this.$input = this.$el.find('input[type=checkbox]').first(); - this.$input.bootstrapToggle(); - // When disable then set tabindex value to -1 - this.$el.find('.toggle.btn') - .attr('tabindex', data.disabled ? '-1' : '0') - .attr('id', data.cId); - - this.$el.find('.toggle.btn .toggle-group .btn').attr('aria-hidden', true); - this.$el.find('div.toggle').append(''); - this.setSrValue(); - - this.updateInvalid(); - - /* Bootstrap toggle does not have option for readonly - * If readonly, then let it focus. - */ - if(data.readonly) { - this.$el.find('.select2-selection').attr('tabindex', 0); - } - return this; - }, - }); - - // Backform Dialog view (in bootstrap tabbular form) - // A collection of field models. - Backform.Dialog = Backform.Form.extend({ - /* Array of objects having attributes [label, fields] */ - schema: undefined, - tagName: 'div', - legend: true, - className: function() { - return 'pg-el-sm-12 pg-el-md-12 pg-el-lg-12 pg-el-12'; - }, - tabPanelClassName: function() { - return Backform.tabClassName; - }, - tabIndex: 0, - initialize: function(opts) { - let s = opts.schema; - if (s && _.isArray(s)) { - this.schema = _.each(s, function(o) { - if (o.fields && !(o.fields instanceof Backbone.Collection)) - o.fields = new Backform.Fields(o.fields); - o.cId = o.cId || _.uniqueId('pgC_'); - o.hId = o.hId || _.uniqueId('pgH_'); - o.disabled = o.disabled || false; - o.legend = opts.legend; - }); - if (opts.tabPanelClassName && _.isFunction(opts.tabPanelClassName)) { - this.tabPanelClassName = opts.tabPanelClassName; - } - } - this.model.errorModel = opts.errorModel || this.model.errorModel || new Backbone.Model(); - this.controls = []; - }, - template: { - 'header': _.template([ - '', - ].join(' ')), - 'panel': _.template( - '
' - ), - }, - render: function() { - this.cleanup(); - - let c = this.$el - .children().first().children('.active') - .first().attr('id'), - m = this.model, - controls = this.controls, - tmpls = this.template, - self = this, - idx = (this.tabIndex * 100), - evalF = function(f, d, model) { - return (_.isFunction(f) ? !!f.apply(d, [model]) : !!f); - }; - - this.$el - .empty() - .attr('role', 'tabpanel') - .attr('class', _.result(this, 'tabPanelClassName')); - m.panelEl = this.$el; - - let tabHead = $('') - .appendTo(this.$el); - let tabContent = $('
') - .appendTo(this.$el); - - _.each(this.schema, function(o) { - idx++; - if (!o.version_compatible || !evalF(o.visible, o, m)) { - return; - } - let el = $((tmpls['panel'])(_.extend(o, { - 'tabIndex': idx, - 'tabPanelCodeClass': o.tabPanelCodeClass ? o.tabPanelCodeClass : '', - }))) - .appendTo(tabContent) - .removeClass('collapse').addClass('collapse'); - $((tmpls['header'])(o)).appendTo(tabHead); - - o.fields.each(function(f) { - let cntr = new(f.get('control'))({ - field: f, - model: m, - dialog: self, - tabIndex: idx, - }); - el.append(cntr.render().$el); - controls.push(cntr); - }); - - tabHead.find('a[data-toggle="tab"]').off( - 'shown.bs.tab' - ).off('hidden.bs.tab').on( - 'hidden.bs.tab', - function() { - self.hidden_tab = $(this).data('tabIndex'); - }).on('shown.bs.tab', function() { - let ctx = this; - ctx.shown_tab = $(ctx).data('tabIndex'); - m.trigger('pg-property-tab-changed', { - 'model': m, - 'shown': ctx.shown_tab, - 'hidden': ctx.hidden_tab, - 'tab': ctx, - }); - }); - }); - - let makeActive = tabHead.find('[id="' + c + '"]').first(); - if (makeActive.length == 1) { - makeActive.addClass('active show'); - tabContent.find('#' + makeActive.attr('aria-controls')) - .addClass('active show'); - } else { - tabHead.find('.nav-link').first().addClass('active show'); - tabContent.find('.tab-pane').first().addClass('active show'); - } - - return this; - }, - remove: function(opts) { - if (opts && opts.data) { - if (this.model) { - if (this.model.reset) { - this.model.reset({ - validate: false, - silent: true, - stop: true, - }); - } - this.model.clear({ - validate: false, - silent: true, - stop: true, - }); - delete(this.model); - } - if (this.errorModel) { - this.errorModel.clear({ - validate: false, - silent: true, - stop: true, - }); - delete(this.errorModel); - } - } - this.cleanup(); - Backform.Form.prototype.remove.apply(this, arguments); - }, - }); - - Backform.Accordian = Backform.Dialog.extend({ - className: function() { - return 'set-group pg-el-12'; - }, - tabPanelClassName: function() { - return Backform.tabClassName; - }, - legendClass: 'badge', - contentClass: Backform.accordianContentClassName + ' collapse show', - template: { - 'header': _.template([ - '
>', - ' <% if (legend != false) { %>', - '
data-target="#<%=cId%>" aria-controls="<%=cId%>" aria-level="3" role="heading"><%=collapse ? "" : "" %><%=label%>', - ' <% } %>', - '
', - ].join('\n')), - 'content': _.template( - '
' - ), - }, - collapse: true, - render: function() { - this.cleanup(); - - let m = this.model, - $el = this.$el, - tmpl = this.template, - controls = this.controls, - data = { - 'className': _.result(this, 'className'), - 'legendClass': _.result(this, 'legendClass'), - 'contentClass': _.result(this, 'contentClass'), - 'collapse': _.result(this, 'collapse'), - }, - idx = (this.tabIndex * 100), - evalF = function(f, d, model) { - return (_.isFunction(f) ? !!f.apply(d, [model]) : !!f); - }; - - this.$el.empty(); - - _.each(this.schema, function(o) { - idx++; - if (!o.version_compatible || !evalF(o.visible, o, m)) { - return; - } - - if (!o.fields) - return; - - let d = _.extend({}, data, o), - h = $((tmpl['header'])(d)).appendTo($el), - el = $((tmpl['content'])(d)).appendTo(h); - - o.fields.each(function(f) { - let cntr = new(f.get('control'))({ - field: f, - model: m, - tabIndex: idx, - }); - el.append(cntr.render().$el); - controls.push(cntr); - }); - }); - - return this; - }, - getValueFromDOM: function() { - return ''; - }, - events: {}, - }); - - Backform.Fieldset = Backform.Dialog.extend({ - className: function() { - return 'set-group pg-el-12'; - }, - tabPanelClassName: function() { - return Backform.tabClassName; - }, - fieldsetClass: Backform.setGroupClassName, - legendClass: 'badge', - contentClass: Backform.setGroupContentClassName + ' collapse show', - template: { - 'header': _.template([ - '
>', - ' <% if (legend != false) { %>', - ' data-target="#<%=cId%>"><%=collapse ? "" : "" %><%=label%>', - ' <% } %>', - '
', - ].join('\n')), - 'content': _.template( - '
' - ), - }, - collapse: true, - render: function() { - this.cleanup(); - - let m = this.model, - $el = this.$el, - tmpl = this.template, - controls = this.controls, - data = { - 'className': _.result(this, 'className'), - 'fieldsetClass': _.result(this, 'fieldsetClass'), - 'legendClass': _.result(this, 'legendClass'), - 'contentClass': _.result(this, 'contentClass'), - 'collapse': _.result(this, 'collapse'), - }, - idx = (this.tabIndex * 100), - evalF = function(f, d, model) { - return (_.isFunction(f) ? !!f.apply(d, [model]) : !!f); - }; - - this.$el.empty(); - - _.each(this.schema, function(o) { - idx++; - if (!o.version_compatible || !evalF(o.visible, o, m)) { - return; - } - - if (!o.fields) - return; - - let d = _.extend({}, data, o), - h = $((tmpl['header'])(d)).appendTo($el), - el = $((tmpl['content'])(d)).appendTo(h); - - o.fields.each(function(f) { - let cntr = new(f.get('control'))({ - field: f, - model: m, - tabIndex: idx, - }); - el.append(cntr.render().$el); - controls.push(cntr); - }); - }); - - return this; - }, - getValueFromDOM: function() { - return ''; - }, - events: {}, - }); - - Backform.generateGridColumnsFromModel = - function(node_info, m, type, cols, node) { - let groups = Backform.generateViewSchema( - node_info, m, type, node, true, true - ), - schema = [], - columns = [], - func, - idx = 0; - - // Create another array if cols is of type object & store its keys in that array, - // If cols is object then chances that we have custom width class attached with in. - if (_.isNull(cols) || _.isUndefined(cols)) { - func = function(f) { - f.cell_priority = idx; - idx = idx + 1; - - // We can also provide custom header cell class in schema itself, - // But we will give priority to extraClass attached in cols - // If headerCell property is already set by cols then skip extraClass property from schema - if (!(f.headerCell) && f.cellHeaderClasses) { - f.headerCell = Backgrid.Extension.CustomHeaderCell; - } - }; - } else if (_.isArray(cols)) { - func = function(f) { - f.cell_priority = _.indexOf(cols, f.name); - - // We can also provide custom header cell class in schema itself, - // But we will give priority to extraClass attached in cols - // If headerCell property is already set by cols then skip extraClass property from schema - if ((!f.headerCell) && f.cellHeaderClasses) { - f.headerCell = Backgrid.Extension.CustomHeaderCell; - } - }; - } else if (_.isObject(cols)) { - let tblCols = Object.keys(cols); - func = function(f) { - let val = (f.name in cols) && cols[f.name], - i; - - if (_.isNull(val) || _.isUndefined(val)) { - f.cell_priority = -1; - return; - } - if (_.isObject(val)) { - if ('index' in val) { - f.cell_priority = val['index']; - idx = (idx > val['index']) ? idx + 1 : val['index']; - } else { - i = _.indexOf(tblCols, f.name); - f.cell_priority = idx = ((i > idx) ? i : idx); - idx = idx + 1; - } - - // We can also provide custom header cell class in schema itself, - // But we will give priority to extraClass attached in cols - // If headerCell property is already set by cols then skip extraClass property from schema - if (!f.headerCell) { - if (f.cellHeaderClasses) { - f.headerCell = Backgrid.Extension.CustomHeaderCell; - } - if ('class' in val && _.isString(val['class'])) { - f.headerCell = Backgrid.Extension.CustomHeaderCell; - f.cellHeaderClasses = (f.cellHeaderClasses || '') + ' ' + val['class']; - } - } - } - if (_.isString(val)) { - i = _.indexOf(tblCols, f.name); - - f.cell_priority = idx = ((i > idx) ? i : idx); - idx = idx + 1; - - if (!f.headerCell) { - f.headerCell = Backgrid.Extension.CustomHeaderCell; - } - f.cellHeaderClasses = (f.cellHeaderClasses || '') + ' ' + val; - } - }; - } - - // Prepare columns for backgrid - _.each(groups, function(group) { - _.each(group.fields, function(f) { - if (!f.cell) { - return; - } - // Check custom property in cols & if it is present then attach it to current cell - func(f); - if (f.cell_priority != -1) { - columns.push(f); - } - }); - schema.push(group); - }); - return { - 'columns': _.sortBy(columns, function(c) { - return c.cell_priority; - }), - 'schema': schema, - }; - }; - - Backform.BinaryPathsGridControl = Backform.Control.extend({ - initialize: function() { - Backform.Control.prototype.initialize.apply(this, arguments); - - let BinaryPathModel = Backbone.Model.extend({ - idAttribute: 'serverType', - defaults: { - serverType: undefined, - binaryPath: undefined, - isDefault: false - }, - }); - - let placeholder = pgAdmin.enable_binary_path_browsing ? gettext('Select binary path...') : gettext('Enter binary path...'), - browse_btn_visible = pgAdmin.enable_binary_path_browsing ? true : false; - - if (pgAdmin.server_mode === 'False') { - placeholder = gettext('Select binary path...'); - browse_btn_visible = true; - } - - this.gridColumns = [{ - name: 'isDefault', - label: gettext('Set as default'), - sortable: false, - cell: Backgrid.RadioCell, - cellHeaderClasses: 'width_percent_10', - headerCell: Backgrid.Extension.CustomHeaderCell, - deps: ['binaryPath'], - editable: function(m) { - if (!_.isUndefined(m.get('binaryPath')) && !_.isNull(m.get('binaryPath')) && m.get('binaryPath') !== '') { - return true; - } else if (!_.isUndefined(m.get('isDefault')) && !_.isNull(m.get('isDefault'))){ - setTimeout(function() { - m.set('isDefault', false); - }, 10); - } - - return false; - } - }, { - name: 'serverType', - label: gettext('Database Server'), - editable: false, - cell: 'string', - cellHeaderClasses: 'width_percent_20', - headerCell: Backgrid.Extension.CustomHeaderCell, - }, { - name: 'binaryPath', - label: gettext('Binary Path'), - sortable: false, - cell: Backgrid.Extension.SelectFileCell, - dialog_type: 'select_folder', - dialog_title: gettext('Select Folder'), - placeholder: placeholder, - browse_btn_label: gettext('Select path'), - check_btn_label: gettext('Validate utilities'), - browse_btn_visible: browse_btn_visible - }]; - - let BinPathCollection = this.BinPathCollection = new (Backbone.Collection.extend({ - model: BinaryPathModel - }))(null); - - let bin_value = JSON.parse(this.model.get(this.field.get('name'))); - this.BinPathCollection.add(bin_value); - this.listenTo(BinPathCollection, 'change', this.binPathCollectionChanged); - }, - - render: function() { - let self = this, - gridHeader = ['
', - ' ' + gettext(this.field.get('label')) + '', - '
', - ].join('\n'), - gridBody = $('
').append(gridHeader); - - self.grid = new Backgrid.Grid({ - columns: self.gridColumns, - collection: self.BinPathCollection, - className: 'backgrid table presentation table-bordered table-noouter-border table-hover', - }); - - this.$el.empty(); - this.$el.append(gridBody.append(self.grid.render().$el)); - this.$el.append(['' + - gettext('Enter the directory in which the psql, pg_dump, pg_dumpall, and pg_restore' + - ' utilities can be found for the corresponding database server version.' + - ' The default path will be used for server versions that do not have a' + - ' path specified.') + ''].join('\n')); - - return this; - }, - binPathCollectionChanged: function() { - let bin_value = JSON.stringify(this.BinPathCollection.toJSON()); - this.model.set(this.field.get('name'), bin_value, { - silent: false - }); - } - }); - - Backform.UniqueColCollectionControl = Backform.Control.extend({ - initialize: function() { - Backform.Control.prototype.initialize.apply(this, arguments); - - let uniqueCol = this.field.get('uniqueCol') || [], - m = this.field.get('model'), - schema = m.prototype.schema || m.__super__.schema, - columns = [], - self = this; - - _.each(schema, function(s) { - columns.push(s.id); - }); - - // Check if unique columns provided are also in model attributes. - if (uniqueCol.length > _.intersection(columns, uniqueCol).length) { - throw new Error('Developer: Unique columns [ ' + _.difference(uniqueCol, columns) + ' ] not found in collection model [ ' + columns + ' ].'); - } - - let collection = self.collection = self.model.get(self.field.get('name')); - - if (!collection) { - collection = self.collection = new(pgAdmin.Browser.Node.Collection)( - null, { - model: self.field.get('model'), - silent: true, - handler: self.model, - top: self.model.top || self.model, - attrName: self.field.get('name'), - }); - self.model.set(self.field.get('name'), collection, { - silent: true, - }); - } - - if (this.field.get('version_compatible')) { - self.listenTo(collection, 'add', self.collectionChanged); - self.listenTo(collection, 'change', self.collectionChanged); - } - }, - cleanup: function() { - this.stopListening(this.collection, 'change', this.collectionChanged); - - if (this.field.get('version_compatible')) { - this.stopListening(self.collection, 'add', this.collectionChanged); - this.stopListening(self.collection, 'change', this.collectionChanged); - } - if (this.grid) { - this.grid.remove(); - delete this.grid; - } - this.$el.empty(); - }, - collectionChanged: function(newModel, coll, op) { - let uniqueCol = this.field.get('uniqueCol') || [], - uniqueChangedAttr = [], - self = this; - - // Check if changed model attributes are also in unique columns. And then only check for uniqueness. - if (newModel.attributes) { - _.each(uniqueCol, function(col) { - if (_.has(newModel.attributes, col)) { - uniqueChangedAttr.push(col); - } - }); - if (uniqueChangedAttr.length == 0) { - return; - } - } else { - return; - } - - let collection = this.model.get(this.field.get('name')); - this.stopListening(collection, 'change', this.collectionChanged); - // Check if changed attribute's value of new/updated model also exist for another model in collection. - // If duplicate value exists then set the attribute's value of new/updated model to its previous values. - let m = undefined, - oldModel = undefined; - collection.each(function(model) { - if (newModel != model) { - let duplicateAttrValues = []; - _.each(uniqueCol, function(attr) { - let attrValue = newModel.get(attr); - if (!_.isUndefined(attrValue) && attrValue == model.get(attr)) { - duplicateAttrValues.push(attrValue); - } - }); - if (duplicateAttrValues.length == uniqueCol.length) { - m = newModel; - // Keep reference of model to make it visible in dialog. - oldModel = model; - } - } - }); - if (m) { - if (op && op.add) { - // Remove duplicate model. - setTimeout(function() { - collection.remove(m); - }, 0); - - } else { - /* - * Set model value to its previous value as its new value is - * conflicting with another model value. - */ - - m.set(uniqueChangedAttr[0], m.previous(uniqueChangedAttr[0])); - } - if (oldModel) { - let idx = collection.indexOf(oldModel); - if (idx > -1) { - let newRow = self.grid.body.rows[idx].$el; - newRow.addClass('new'); - $(newRow).pgMakeVisible('backform-tab'); - setTimeout(function() { - newRow.removeClass('new'); - }, 3000); - } - } - } - - this.listenTo(collection, 'change', this.collectionChanged); - }, - render: function() { - // Clean up existing elements - - this.undelegateEvents(); - this.$el.empty(); - - let field = _.defaults(this.field.toJSON(), this.defaults), - attributes = this.model.toJSON(), - attrArr = field.name.split('.'), - name = attrArr.shift(), - path = attrArr.join('.'), - rawValue = this.keyPathAccessor(attributes[name], path), - data = _.extend(field, { - rawValue: rawValue, - value: this.formatter.fromRaw(rawValue, this.model), - attributes: attributes, - formatter: this.formatter, - }), - evalF = function(f, d, m) { - return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f); - }; - - // Evaluate the disabled, visible, required, canAdd, & canDelete option - _.extend(data, { - disabled: (field.version_compatible && - evalF.apply(this.field, [data.disabled, data, this.model]) - ), - visible: evalF.apply(this.field, [data.visible, data, this.model]), - required: evalF.apply(this.field, [data.required, data, this.model]), - canAdd: (field.version_compatible && - evalF.apply(this.field, [data.canAdd, data, this.model]) - ), - canAddRow: data.canAddRow, - canDelete: evalF.apply(this.field, [data.canDelete, data, this.model]), - canEdit: evalF.apply(this.field, [data.canEdit, data, this.model]), - }); - _.extend(data, { - add_label: '', - }); - - // This control is not visible, we should remove it. - if (!data.visible) { - return this; - } - - this.control_data = _.clone(data); - - // Show Backgrid Control - let grid = this.showGridControl(data); - - this.$el.html(grid).addClass(field.name); - this.updateInvalid(); - - this.delegateEvents(); - return this; - }, - showGridControl: function(data) { - data.cId = data.cId || _.uniqueId('pgC_'); - let self = this, - gridHeader = _.template([ - '
', - ' <%-label%>', - ' ', - '
', - ].join('\n')), - gridBody = $('
').append( - gridHeader(data) - ); - - // Clean up existing grid if any (in case of re-render) - if (self.grid) { - self.grid.remove(); - } - - if (!(data.subnode)) { - return ''; - } - - let subnode = data.subnode.schema ? data.subnode : data.subnode.prototype, - gridSchema = Backform.generateGridColumnsFromModel( - data.node_info, subnode, this.field.get('mode'), data.columns - ); - - // Set visibility of Add button - if (data.mode == 'properties') { - $(gridBody).find('button.add').remove(); - } - - // Insert Delete Cell into Grid - if (!data.disabled && data.canDelete) { - gridSchema.columns.unshift({ - name: 'pg-backform-delete', - label: '', - cell: Backgrid.Extension.DeleteCell, - editable: false, - cell_priority: -1, - canDeleteRow: data.canDeleteRow, - }); - } - - // Insert Edit Cell into Grid - if (!data.disabled && data.canEdit) { - let editCell = Backgrid.Extension.ObjectCell.extend({ - schema: gridSchema.schema, - }); - - gridSchema.columns.unshift({ - name: 'pg-backform-edit', - label: '', - cell: editCell, - cell_priority: -2, - canEditRow: data.canEditRow, - }); - } - - let collection = this.model.get(data.name); - - let cellEditing = function(args) { - let that = this, - cell = args[0]; - // Search for any other rows which are open. - this.each(function(m) { - // Check if row which we are about to close is not current row. - if (cell.model != m) { - let idx = that.indexOf(m); - if (idx > -1) { - let row = self.grid.body.rows[idx], - rowEditCell = row.$el.find('.subnode-edit-in-process').parent(); - // Only close row if it's open. - if (rowEditCell.length > 0) { - let event = new Event('click'); - rowEditCell[0].dispatchEvent(event); - } - } - } - }); - }; - // Listen for any row which is about to enter in edit mode. - collection.on('enteringEditMode', cellEditing, collection); - - // Initialize a new Grid instance - self.grid = new Backgrid.Grid({ - columns: gridSchema.columns, - collection: collection, - className: 'backgrid table presentation table-bordered table-noouter-border table-hover', - attr: { - 'aria-labelledby': data.cId, - }, - }); - - for(let i = 0; i < (collection.length); i++) { - collection.at(i).parentTr = self.grid.body.rows[i].$el; - } - - // Render subNode grid - let subNodeGrid = self.grid.render().$el; - - // Combine Edit and Delete Cell - if (data.canDelete && data.canEdit) { - $(subNodeGrid).find('th.pg-backform-delete').remove(); - $(subNodeGrid).find('th.pg-backform-edit').attr('colspan', '2'); - } - - let $dialog = gridBody.append(subNodeGrid); - - let tmp_browser = pgBrowser; - if (pgBrowser.preferences_cache.length == 0) - tmp_browser = pgWindow.default.pgAdmin.Browser; - - let preferences = tmp_browser.get_preferences_for_module('browser'); - - if (preferences) { - let addBtn = $dialog.find('.add'); - // Add title to the buttons - $(addBtn) - .attr('title', - keyboardShortcuts.shortcut_title(gettext('Add new row'),preferences.add_grid_row)); - } - - // Add button callback - if (!(data.disabled || !data.canAdd)) { - $dialog.find('button.add').first().on('click',(e) => { - e.preventDefault(); - let canAddRow = _.isFunction(data.canAddRow) ? - data.canAddRow.apply(self, [self.model]) : true; - if (canAddRow) { - // Close any existing expanded row before adding new one. - _.each(self.grid.body.rows, function(row) { - let rowEditCell = row.$el.find('.subnode-edit-in-process').parent(); - // Only close row if it's open. - if (rowEditCell.length > 0) { - let event = new Event('click'); - rowEditCell[0].dispatchEvent(event); - } - }); - - let allowMultipleEmptyRows = !!self.field.get('allowMultipleEmptyRows'); - - // If allowMultipleEmptyRows is not set or is false then don't allow second new empty row. - // There should be only one empty row. - if (!allowMultipleEmptyRows && collection) { - let isEmpty = false; - collection.each(function(model) { - let modelValues = []; - _.each(model.attributes, function(val) { - modelValues.push(val); - }); - if (!_.some(modelValues, _.identity)) { - isEmpty = true; - } - }); - if (isEmpty) { - return false; - } - } - - $(self.grid.body.$el.find($('tr.new'))).removeClass('new'); - let m = new(data.model)(null, { - silent: true, - handler: collection, - top: self.model.top || self.model, - collection: collection, - node_info: self.model.node_info, - }); - - if(data.beforeAdd) { - m = data.beforeAdd.apply(self, [m]); - } - collection.add(m); - - let idx = collection.indexOf(m), - newRow = self.grid.body.rows[idx].$el; - - collection.get(m).parentTr = newRow; - m.parentTr = newRow; - - newRow.addClass('new'); - if(!$(newRow).pgMakeBackgridVisible('.backform-tab')){ - // We can have subnode controls in Panels - $(newRow).pgMakeBackgridVisible('.set-group'); - } - return false; - } - }); - } - - return $dialog; - }, - clearInvalid: function() { - this.$el.removeClass('subnode-error'); - this.$el.find('.pgadmin-control-error-message').remove(); - return this; - }, - updateInvalid: function() { - let self = this, - errorModel = self.model.errorModel; - - if (!(errorModel instanceof Backbone.Model)) return this; - - this.clearInvalid(); - }, - }); - - Backform.SubNodeCollectionControl = Backform.Control.extend({ - row: Backgrid.Row, - render: function() { - let field = _.defaults(this.field.toJSON(), this.defaults), - attributes = this.model.toJSON(), - attrArr = field.name.split('.'), - name = attrArr.shift(), - path = attrArr.join('.'), - rawValue = this.keyPathAccessor(attributes[name], path), - data = _.extend(field, { - rawValue: rawValue, - value: this.formatter.fromRaw(rawValue, this.model), - attributes: attributes, - formatter: this.formatter, - }), - evalF = function(f, d, m) { - return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f); - }; - - // Evaluate the disabled, visible, required, canAdd, cannEdit & canDelete option - _.extend(data, { - disabled: evalF(data.disabled, data, this.model), - visible: evalF(data.visible, data, this.model), - required: evalF(data.required, data, this.model), - canAdd: evalF(data.canAdd, data, this.model), - canAddRow: data.canAddRow, - canEdit: evalF(data.canEdit, data, this.model), - canDelete: evalF(data.canDelete, data, this.model), - showError: data.showError || true, - }); - // Show Backgrid Control - let grid = (data.subnode == undefined) ? '' : this.showGridControl(data); - - // Clean up first - this.$el.removeClass(Backform.hiddenClassName); - - if (!data.visible) - this.$el.addClass(Backform.hiddenClassName); - - this.$el.html(grid).addClass(field.name); - this.updateInvalid(); - - return this; - }, - updateInvalid: function() { - let self = this; - let errorModel = this.model.errorModel; - if (!(errorModel instanceof Backbone.Model)) return this; - - this.clearInvalid(); - - let attrArr = self.field.get('name').split('.'), - path = attrArr.join('.'), - error = self.keyPathAccessor(errorModel.toJSON(), path); - - if (_.isEmpty(error)) return; - - if (self.field.get('showError')) { - self.$el.addClass('subnode-error').append( - $('
').addClass('pgadmin-control-error-message pg-el-offset-4 pg-el-8 help-block').text(error) - ); - } - }, - cleanup: function() { - // Clean up existing grid if any (in case of re-render) - if (this.grid) { - this.grid.remove(); - } - if (this.collection) { - this.collection.off('enteringEditMode'); - } - }, - clearInvalid: function() { - if (this.field.get('showError')) { - this.$el.removeClass('subnode-error'); - this.$el.find('.pgadmin-control-error-message').remove(); - } - return this; - }, - showGridControl: function(data) { - let self = this, - gridHeader = ['
', - ' ' + data.label + '', - ' ', - '
', - ].join('\n'), - gridBody = $('
').append(gridHeader); - - let subnode = data.subnode.schema ? data.subnode : data.subnode.prototype, - gridSchema = Backform.generateGridColumnsFromModel( - data.node_info, subnode, this.field.get('mode'), data.columns, data.schema_node - ); - - // Clean up existing grid if any (in case of re-render) - if (self.grid) { - self.grid.remove(); - } - - // Set visibility of Add button - if (data.disabled || !data.canAdd) { - $(gridBody).find('button.add').remove(); - } - - // Insert Delete Cell into Grid - if (!data.disabled && data.canDelete) { - gridSchema.columns.unshift({ - name: 'pg-backform-delete', - label: '', - cell: Backgrid.Extension.DeleteCell, - editable: false, - cell_priority: -1, - canDeleteRow: data.canDeleteRow, - customDeleteMsg: data.customDeleteMsg, - customDeleteTitle: data.customDeleteTitle, - }); - } - - // Insert Edit Cell into Grid - if (!data.disabled && data.canEdit) { - let editCell = Backgrid.Extension.ObjectCell.extend({ - schema: gridSchema.schema, - }), - canEdit = self.field.has('canEdit') && - self.field.get('canEdit') || true; - - gridSchema.columns.unshift({ - name: 'pg-backform-edit', - label: '', - cell: editCell, - cell_priority: -2, - editable: canEdit, - canEditRow: data.canEditRow, - }); - } - - let collection = self.model.get(data.name); - - if (!collection) { - collection = new(pgBrowser.Node.Collection)(null, { - handler: self.model.handler || self.model, - model: data.model, - top: self.model.top || self.model, - silent: true, - }); - self.model.set(data.name, collection, { - silent: true, - }); - } - - let cellEditing = function(args) { - let ctx = this, - cell = args[0]; - // Search for any other rows which are open. - this.each(function(m) { - // Check if row which we are about to close is not current row. - if (cell.model != m) { - let idx = ctx.indexOf(m); - if (idx > -1) { - let row = grid.body.rows[idx], - rowEditCell = row.$el.find('.subnode-edit-in-process').parent(); - // Only close row if it's open. - if (rowEditCell.length > 0) { - let event = new Event('click'); - rowEditCell[0].dispatchEvent(event); - } - } - } - }); - }; - // Listen for any row which is about to enter in edit mode. - collection.on('enteringEditMode', cellEditing, collection); - - // Initialize a new Grid instance - let grid = self.grid = new Backgrid.Grid({ - columns: gridSchema.columns, - collection: collection, - row: this.row, - className: 'backgrid table presentation table-bordered table-noouter-border table-hover', - }); - - // Render subNode grid - let subNodeGrid = grid.render().$el; - - // Combine Edit and Delete Cell - if (data.canDelete && data.canEdit) { - $(subNodeGrid).find('th.pg-backform-delete').remove(); - $(subNodeGrid).find('th.pg-backform-edit').attr('colspan', '2'); - } - - let $dialog = gridBody.append(subNodeGrid); - - let tmp_browser = pgBrowser; - if (pgBrowser.preferences_cache.length == 0) - tmp_browser = pgWindow.default.pgAdmin.Browser; - - let preferences = tmp_browser.get_preferences_for_module('browser'); - - if (preferences) { - let addBtn = $dialog.find('.add'); - // Add title to the buttons - $(addBtn) - .attr('title', - keyboardShortcuts.shortcut_title(gettext('Add new row'),preferences.add_grid_row)); - } - - - // Add button callback - $dialog.find('button.add').on('click',(e) => { - e.preventDefault(); - let canAddRow = _.isFunction(data.canAddRow) ? - data.canAddRow.apply(self, [self.model]) : true; - if (canAddRow) { - // Close any existing expanded row before adding new one. - _.each(grid.body.rows, function(row) { - let rowEditCell = row.$el.find('.subnode-edit-in-process').parent(); - // Only close row if it's open. - if (rowEditCell.length > 0) { - let event = new Event('click'); - rowEditCell[0].dispatchEvent(event); - } - }); - - grid.insertRow({}); - - let newRow = $(grid.body.rows[collection.length - 1].$el); - newRow.attr('class', 'new').on('click',() => { - $(this).attr('class', 'editable'); - }); - - if(!$(newRow).pgMakeBackgridVisible('.backform-tab')){ - // We can have subnode controls in Panels - $(newRow).pgMakeBackgridVisible('.set-group'); - } - return false; - } - }); - - return $dialog; - }, - }); - - /* - * SQL Tab Control for showing the modified SQL for the node with the - * property 'hasSQL' is set to true. - * - * When the user clicks on the SQL tab, we will send the modified data to the - * server and fetch the SQL for it. - */ - Backform.SqlTabControl = Backform.Control.extend({ - defaults: { - label: '', - controlsClassName: 'pgadmin-controls pg-el-sm-12 SQL', - extraClasses: [], - helpMessage: null, - }, - template: _.template([ - '', - '
', - ' ', - ' <% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - ' <% } %>', - '
', - ].join('\n')), - /* - * Initialize the SQL Tab control properly - */ - initialize: function(o) { - Backform.Control.prototype.initialize.apply(this, arguments); - - // Save the required information for using it later. - this.dialog = o.dialog; - this.tabIndex = o.tabIndex; - - _.bindAll(this, 'onTabChange', 'onPanelResized'); - }, - getValueFromDOM: function() { - return this.formatter.toRaw(this.$el.find('textarea').val(), this.model); - }, - - reflectPreferences: function() { - let self = this; - /* self.sqlCtrl is null when SQL tab is not active */ - if(self.sqlCtrl) { - let sqlEditPreferences = pgAdmin.Browser.get_preferences_for_module('sqleditor'); - - $(self.sqlCtrl.getWrapperElement()).css( - 'font-size', calcFontSize(sqlEditPreferences.sql_font_size) - ); - self.sqlCtrl.setOption('tabSize', sqlEditPreferences.tab_size); - self.sqlCtrl.setOption('lineWrapping', sqlEditPreferences.wrap_code); - self.sqlCtrl.setOption('autoCloseBrackets', sqlEditPreferences.insert_pair_brackets); - self.sqlCtrl.setOption('matchBrackets', sqlEditPreferences.brace_matching); - self.sqlCtrl.refresh(); - } - }, - render: function() { - if (this.sqlCtrl) { - this.sqlCtrl.toTextArea(); - delete this.sqlCtrl; - this.sqlCtrl = null; - this.$el.empty(); - this.model.off('pg-property-tab-changed', this.onTabChange, this); - this.model.off('pg-browser-resized', this.onPanelResized, this); - } - // Use the Backform Control's render function - Backform.Control.prototype.render.apply(this, arguments); - - this.sqlCtrl = CodeMirror.fromTextArea( - (this.$el.find('textarea')[0]), { - lineNumbers: true, - mode: 'text/x-pgsql', - readOnly: true, - extraKeys: pgAdmin.Browser.editor_shortcut_keys, - screenReaderLabel: 'SQL', - }); - - this.reflectPreferences(); - - /* Check for sql editor preference changes */ - let self = this; - this.$el.find('textarea').attr('tabindex', -1); - pgBrowser.onPreferencesChange('sqleditor', function() { - self.reflectPreferences(); - }); - - /* - * We will listen to the tab change event to check, if the SQL tab has - * been clicked or, not. - */ - this.model.on('pg-property-tab-changed', this.onTabChange, this); - this.model.on('pg-browser-resized', this.onPanelResized, this); - - return this; - }, - onTabChange: function(obj) { - - // Fetch the information only if the SQL tab is visible at the moment. - if (this.dialog && obj.shown == this.tabIndex) { - - // We will send a request to the sever only if something has changed - // in a model and also it does not contain any error. - if (this.model.sessChanged()) { - if (_.size(this.model.errorModel.attributes) == 0) { - let self = this, - node = self.field.get('schema_node'), - msql_url = node.generate_url.apply( - node, [ - null, 'msql', this.field.get('node_data'), !self.model.isNew(), - this.field.get('node_info'), node.url_jump_after_node, - ]); - - // Fetching the modified SQL - self.model.trigger('pgadmin-view:msql:fetching', self.method, node); - - $.ajax({ - url: msql_url, - type: 'GET', - cache: false, - data: self.model.toJSON(true, 'GET'), - dataType: 'json', - contentType: 'application/json', - }).done(function(res) { - self.sqlCtrl.clearHistory(); - self.sqlCtrl.setValue(res.data); - }).fail(function() { - self.model.trigger('pgadmin-view:msql:error', self.method, node, arguments); - }).always(function() { - self.model.trigger('pgadmin-view:msql:fetched', self.method, node, arguments); - }); - } else { - this.sqlCtrl.clearHistory(); - this.sqlCtrl.setValue('-- ' + gettext('Definition incomplete.')); - } - } else { - this.sqlCtrl.clearHistory(); - this.sqlCtrl.setValue('-- ' + gettext('No updates.')); - } - this.sqlCtrl.refresh.apply(this.sqlCtrl); - } - }, - onPanelResized: function(o) { - if (o && o.container) { - let $tabContent = o.container.find( - '.backform-tab > .tab-content' - ).first(), - $sqlPane = $tabContent.find( - 'div[role=tabpanel].tab-pane.SQL' - ); - if ($sqlPane.hasClass('active')) { - $sqlPane.find('.CodeMirror').css( - 'cssText', - 'height: ' + ($tabContent.height() + 8) + 'px !important;' - ); - } - } - }, - remove: function() { - if (this.sqlCtrl) { - this.sqlCtrl.toTextArea(); - delete this.sqlCtrl; - this.sqlCtrl = null; - - this.$el.empty(); - } - this.model.off('pg-property-tab-changed', this.onTabChange, this); - this.model.off('pg-browser-resized', this.onPanelResized, this); - - Backform.Control.__super__.remove.apply(this, arguments); - }, - }); - /* - * Numeric input Control functionality just like backgrid - */ - Backform.NumericControl = Backform.InputControl.extend({ - defaults: { - type: 'number', - label: '', - min: undefined, - max: undefined, - maxlength: 255, - extraClasses: [], - helpMessage: null, - }, - template: _.template([ - '', - '
', - ' <%=readonly ? "readonly aria-readonly=true" : ""%> <%=required ? "required" : ""%> />', - ' <% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - ' <% } %>', - '
', - ].join('\n')), - }); - - /////// - // Generate a schema (as group members) based on the model's schema - // - // It will be used by the grid, properties, and dialog view generation - // functions. - Backform.generateViewSchema = function( - node_info, Model, mode, node, treeData, noSQL, subschema - ) { - let proto = (Model && Model.prototype) || Model, - schema = subschema || (proto && proto.schema), - fields = [], - groupInfo = {}; - - // 'schema' has the information about how to generate the form. - if (schema && _.isArray(schema)) { - let evalASFunc = evalASFunc = function(prop) { - return ((prop && proto[prop] && - typeof proto[prop] == 'function') ? proto[prop] : prop); - }; - let groups = {}, - server_info = node_info && ('server' in node_info) && - pgBrowser.serverInfo && pgBrowser.serverInfo[node_info.server._id], - in_catalog = node_info && ('catalog' in node_info), - ver_in_limit; - - _.each(schema, function(s) { - // Do we understand - what control, we're creating - // here? - if (s.type == 'group') { - let visible = true; - if (_.isUndefined(server_info)) { - ver_in_limit = true; - } else { - ver_in_limit = ((_.isUndefined(s.server_type) ? true : - (server_info.type in s.server_type)) && - (_.isUndefined(s.min_version) ? true : - (server_info.version >= s.min_version)) && - (_.isUndefined(s.max_version) ? true : - (server_info.version <= s.max_version))); - } - - if (s.mode && _.isObject(s.mode)) - visible = (_.indexOf(s.mode, mode) > -1); - if (visible) - visible = evalASFunc(s.visible); - - groupInfo[s.id] = { - label: s.label || s.id, - version_compatible: ver_in_limit, - visible: visible, - tabPanelCodeClass: '', - }; - return; - } - - if (!s.mode || (s && s.mode && _.isObject(s.mode) && - _.indexOf(s.mode, mode) != -1)) { - // Each field is kept in specified group, or in - // 'General' category. - let group = s.group || gettext('General'), - control = s.control || Backform.getMappedControl(s.type, mode), - cell = s.cell || Backform.getMappedControl(s.type, 'cell'); - - if (control == null) { - return; - } - - // Generate the empty group list (if not exists) - groups[group] = (groups[group] || []); - if (_.isUndefined(server_info)) { - ver_in_limit = true; - } else { - ver_in_limit = ((_.isUndefined(s.server_type) ? true : - (server_info.type in s.server_type)) && - (_.isUndefined(s.min_version) ? true : - (server_info.version >= s.min_version)) && - (_.isUndefined(s.max_version) ? true : - (server_info.version <= s.max_version))); - } - - let disabled = ( - !ver_in_limit || in_catalog - ), - readonly = (mode == 'properties'), - schema_node = (s.node && _.isString(s.node) && - s.node in pgBrowser.Nodes && pgBrowser.Nodes[s.node]) || node; - - let o = _.extend(_.clone(s), { - name: s.id, - // This can be disabled in some cases (if not hidden) - - disabled: (disabled ? true : evalASFunc(s.disabled)), - readonly: (readonly ? true : evalASFunc(s.readonly)), - editable: _.isUndefined(s.editable) ? - pgAdmin.editableCell : evalASFunc(s.editable), - subnode: ((_.isString(s.model) && s.model in pgBrowser.Nodes) ? - pgBrowser.Nodes[s.model].model : s.model), - canAdd: (disabled || readonly) ? false : evalASFunc(s.canAdd), - canAddRow: (disabled || readonly) ? false : evalASFunc(s.canAddRow), - canEdit: (disabled || readonly) ? false : evalASFunc(s.canEdit), - canDelete: (disabled || readonly) ? false : evalASFunc(s.canDelete), - canEditRow: (disabled || readonly) ? false : evalASFunc(s.canEditRow), - canDeleteRow: (disabled || readonly) ? false : evalASFunc(s.canDeleteRow), - transform: evalASFunc(s.transform), - mode: mode, - control: control, - cell: cell, - node_info: node_info, - schema_node: schema_node, - // Do we need to show this control in this mode? - visible: evalASFunc(s.visible), - node: node, - node_data: treeData, - version_compatible: ver_in_limit, - }); - delete o.id; - - // Temporarily store in dictionary format for - // utilizing it later. - groups[group].push(o); - - if (s.type == 'nested') { - delete o.name; - delete o.cell; - - o.schema = Backform.generateViewSchema( - node_info, Model, mode, node, treeData, true, s.schema - ); - o.control = o.control || 'tab'; - } - } - }); - - // Do we have fields to genreate controls, which we - // understand? - if (_.isEmpty(groups)) { - return null; - } - - if (!noSQL && node && node.hasSQL && (mode == 'create' || mode == 'edit')) { - groups[gettext('SQL')] = [{ - name: 'sql', - visible: true, - disabled: false, - type: 'text', - control: 'sql-tab', - node_info: node_info, - schema_node: node, - node_data: treeData, - }]; - } - - // Create an array from the dictionary with proper required - // structure. - _.each(groups, function(val, key) { - let tabPanelCodeClass = _.pluck(val, 'tabPanelCodeClass'); - if (tabPanelCodeClass) { - tabPanelCodeClass = tabPanelCodeClass.join(' ').trim(); - } - fields.push( - _.extend( - _.defaults( - groupInfo[key] || { - label: key, - tabPanelCodeClass: tabPanelCodeClass, - }, { - version_compatible: true, - visible: true, - } - ), { - fields: val, - }) - ); - }); - } - - return fields; - }; - - let Select2Formatter = function() {/*This is intentional (SonarQube)*/}; - _.extend(Select2Formatter.prototype, { - fromRaw: function(rawData) { - return encodeURIComponent(rawData); - }, - toRaw: function(formattedData, model, opts) { - if (_.isArray(formattedData)) { - if (opts && opts.tags) - return formattedData; - return _.map(formattedData, decodeURIComponent); - } else { - if (!_.isNull(formattedData) && !_.isUndefined(formattedData)) { - return decodeURIComponent(formattedData); - } else { - return null; - } - } - }, - }); - - /* - * Backform Select2 control. - */ - Backform.Select2Control = Backform.SelectControl.extend({ - defaults: _.extend({}, Backform.SelectControl.prototype.defaults, { - select2: { - first_empty: true, - multiple: false, - emptyOptions: false, - preserveSelectionOrder: false, - isDropdownParent: false, - }, - // To accept the label and conrol classes while extending control if - // required(e.g. if we want to show label and control in 50-50% or in - //different width of dialog/form) otherwise default classes will be added - controlLabelClassName: Backform.controlLabelClassName, - controlsClassName: Backform.controlsClassName, - }), - - events: function() { - // Inherit all default events of InputControl - return _.extend({}, Backform.SelectControl.prototype.events, { - 'select2:select': 'onSelect', - }); - }, - - onSelect: function (evt) { - let sel2Options = this.field.get('select2'); - if (!_.isUndefined(sel2Options) && !_.isNull(sel2Options) && - sel2Options.multiple && sel2Options.preserveSelectionOrder) { - let element = evt.params.data.element; - let $element = $(element); - - $element.detach(); - $(this.$sel).append($element); - $(this.$sel).trigger('change'); - } - - let new_value = _.findWhere(this.field.get('options'), {value: evt.params.data.id}); - if(new_value && !_.isUndefined(new_value.preview_src) && new_value.preview_src) { - this.$el.find('.preview-img img').attr('src', new_value.preview_src); - } - }, - - formatter: Select2Formatter, - template: _.template([ - '<% if(label == false) {} else {%>', - ' ', - '<% }%>', - '
', - ' ', - ' <% if (helpMessage && helpMessage.length) { %>', - ' <%=helpMessage%>', - ' <% } %>', - ' <% for (let i=0; i < options.length; i++) {%>', - ' <% let option = options[i]; %>', - ' <% if (option.preview_src && option.value === rawValue) { %>', - '
', - ' '+gettext('Preview not available...')+'', - '
', - ' <%}%>', - ' <%}%>', - '
', - ].join('\n')), - render: function() { - if (this.$sel && this.$sel.select2 && - this.$sel.select2.hasOwnProperty('destroy')) { - this.$sel.select2('destroy'); - } - - let field = _.defaults(this.field.toJSON(), this.defaults), - attributes = this.model.toJSON(), - attrArr = field.name.split('.'), - name = attrArr.shift(), - path = attrArr.join('.'), - rawValue = this.keyPathAccessor(attributes[name], path), - data = _.extend(field, { - rawValue: rawValue, - value: this.formatter.fromRaw(rawValue, this.model), - attributes: attributes, - formatter: this.formatter, - }), - evalF = function(f, d, m) { - return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f); - }; - - data.select2 = data.select2 || {}; - _.defaults(data.select2, this.defaults.select2, { - first_empty: true, - multiple: false, - emptyOptions: false, - preserveSelectionOrder: false, - isDropdownParent: false, - showOnScroll: true, - }); - - // Evaluate the disabled, visible, and required option - // disable for readonly also and later handle readonly programmatically. - _.extend(data, { - disabled: evalF(data.disabled, data, this.model), - readonly: evalF(data.readonly, data, this.model), - visible: evalF(data.visible, data, this.model), - required: evalF(data.required, data, this.model), - }); - - // Evaluation the options - if (_.isFunction(data.options)) { - try { - data.options = data.options(this); - } catch (e) { - // Do nothing - data.options = []; - this.model.trigger( - 'pgadmin-view:transform:error', this.model, this.field, e - ); - } - } - - data.cId = data.cId || _.uniqueId('pgC_'); - // Clean up first - this.$el.removeClass(Backform.hiddenClassName); - - if (!data.visible) - this.$el.addClass(Backform.hiddenClassName); - - this.$el.html(this.template(data)).addClass(field.name); - - let select2Opts = _.extend({ - disabled: data.disabled || data.readonly, - }, field.select2, { - options: (this.field.get('options') || this.defaults.options), - }); - - // Dropdown body can be render at user given location - // If isDropdownParent flag is set to true then, By default we will - // display it on the control itself. - if (data.select2.isDropdownParent) { - select2Opts.dropdownParent = data.select2.dropdownParent || this.$el; - } - - // If disabled then no need to show placeholder - if (data.disabled || data.readonly) { - select2Opts['placeholder'] = ''; - } - - /* - * Add empty option as Select2 requires any empty '', - ].join(''), - null, { - variable: null, - }), - - initialize: function() { - Backgrid.SelectCell.prototype.initialize.apply(this, arguments); - this.onSave = this.onSave.bind(this); - this.enterEditMode = this.enterEditMode.bind(this); - this.exitEditMode = this.exitEditMode.bind(this); - }, - - render: function() { - var col = _.defaults(this.column.toJSON(), this.defaults), - model = this.model, - column = this.column, - editable = Backgrid.callByNeed(col.editable, column, model), - optionValues = _.clone(this.optionValues || - (_.isFunction(this.column.get('options')) ? - (this.column.get('options'))(this) : - this.column.get('options'))); - - this.undelegateEvents(); - - if (this.$select) { - if (this.$select.data('select2')) { - this.$select.select2('destroy'); - } - delete this.$select; - this.$select = null; - } - - this.$el.empty(); - - if (!_.isArray(optionValues)) - throw new TypeError('optionValues must be an array'); - - var optionText = null, - optionValue = null, - self = this, - selectedValues = model.get(this.column.get('name')), - select2_opts = _.extend({ - openOnEnter: false, - multiple: false, - showOnScroll: true, - first_empty: true, - }, self.defaults.select2, - (col.select2 || {}) - ), - selectTpl = _.template(''); - - var $select = self.$select = $(selectTpl({ - multiple: select2_opts.multiple, - })).appendTo(self.$el); - - /* - * Add empty option as Select2 requires any empty '