diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js
index 5199bc8e..62edc129 100644
--- a/web/pgadmin/browser/static/js/browser.js
+++ b/web/pgadmin/browser/static/js/browser.js
@@ -189,7 +189,7 @@ define('pgadmin.browser', [
width: 500,
isCloseable: false,
isPrivate: true,
- content: '
' + select_object_msg + '
',
+ content: '' + select_object_msg + '
',
events: panelEvents,
}),
// Dependents of the object
@@ -200,7 +200,7 @@ define('pgadmin.browser', [
width: 500,
isCloseable: false,
isPrivate: true,
- content: '' + select_object_msg + '
',
+ content: '' + select_object_msg + '
',
events: panelEvents,
}),
},
diff --git a/web/pgadmin/browser/static/js/collection.js b/web/pgadmin/browser/static/js/collection.js
index 6722b7c5..15d3f613 100644
--- a/web/pgadmin/browser/static/js/collection.js
+++ b/web/pgadmin/browser/static/js/collection.js
@@ -42,6 +42,7 @@ define([
if (this.node_initialized)
return;
this.node_initialized = true;
+
pgAdmin.Browser.add_menus([{
name: 'refresh', node: this.type, module: this,
applies: ['object', 'context'], callback: 'refresh',
@@ -77,12 +78,9 @@ define([
content = $('')
.addClass('pg-prop-content col-12 has-pg-prop-btn-group'),
node = pgBrowser.Nodes[that.node],
+ $msgContainer = '',
// This will be the URL, used for object manipulation.
urlBase = this.generate_url(item, 'properties', data),
- collection = new (node.Collection.extend({
- url: urlBase,
- model: node.model,
- }))(),
info = this.getTreeNodeHierarchy.apply(this, [item]),
gridSchema = Backform.generateGridColumnsFromModel(
info, node.model, 'properties', that.columns
@@ -132,6 +130,10 @@ define([
return null;
}.bind(panel);
+ that.collection = new (node.Collection.extend({
+ url: urlBase,
+ model: node.model,
+ }))();
// Add the new column for the multi-select menus
if((_.isFunction(that.canDrop) ?
that.canDrop.apply(that, [data, item]) : that.canDrop) ||
@@ -180,7 +182,7 @@ define([
that.grid = new Backgrid.Grid({
emptyText: 'No data found',
columns: gridSchema.columns,
- collection: collection,
+ collection: that.collection,
className: 'backgrid table presentation table-bordered table-noouter-border table-hover',
});
@@ -199,6 +201,7 @@ define([
};
if (view) {
+
// Avoid unnecessary reloads
if (_.isEqual($(panel).data('node-prop'), urlBase)) {
return;
@@ -215,9 +218,14 @@ define([
j.empty();
j.data('obj-view', gridView);
+ $msgContainer = '' +
+ gettext('Retrieving data from the server...') + '
';
+
+ $msgContainer = $($msgContainer).appendTo(j);
+
that.header = $('').addClass(
'pg-prop-header'
- ).appendTo(j);
+ );
// Render the buttons
var buttons = [];
@@ -255,26 +263,69 @@ define([
// Render subNode grid
content.append('');
content.find('.pg-prop-coll-container').append(that.grid.render().$el);
- j.append(content);
-
- // Fetch Data
- collection.fetch({
- reset: true,
- error: function(model, error, xhr) {
- pgBrowser.Events.trigger(
- 'pgadmin:collection:retrieval:error', 'properties', xhr, error,
- error.message, item, that
- );
- if (!Alertify.pgHandleItemError(
- xhr, error, error.message, {item: item, info: info}
- )) {
- Alertify.pgNotifier(error, xhr, S(
- gettext('Error retrieving properties - %s.')
- ).sprintf(error.message || that.label).value(), function() {
+
+ var timer;
+
+ $.ajax({
+ url: urlBase,
+ type: 'GET',
+ beforeSend: function() {
+ // Generate a timer for the request
+ timer = setTimeout(function() {
+ // notify user if request is taking longer than 1 second
+
+ $msgContainer.text(gettext('Retrieving data from the server...'));
+ $msgContainer.removeClass('d-none');
+ if (self.grid) {
+ self.grid.remove();
+ }
+ }, 1000);
+ },
+ })
+ .done(function(res) {
+ clearTimeout(timer);
+
+ if (_.isUndefined(that.grid) || _.isNull(that.grid)) return;
+
+ that.data = res;
+
+ if (that.data.length > 0) {
+
+ if (!$msgContainer.hasClass('d-none')) {
+ $msgContainer.addClass('d-none');
+ }
+ that.header.appendTo(j);
+ j.append(content);
+
+ // Listen scroll event to load more rows
+ $('.pg-prop-content').on('scroll', that.__loadMoreRows.bind(that));
+
+ that.collection.reset(that.data.splice(0, 50));
+ } else {
+ // Do not listen the scroll event
+ $('.pg-prop-content').off('scroll', that.__loadMoreRows);
+
+ $msgContainer.text(gettext('No properties are available for the selected object.'));
+
+ }
+ })
+ .fail(function(xhr, error) {
+ pgBrowser.Events.trigger(
+ 'pgadmin:node:retrieval:error', 'properties', xhr, error.message, item, that
+ );
+ if (!Alertify.pgHandleItemError(xhr, error.message, {
+ item: item,
+ info: info,
+ })) {
+ Alertify.pgNotifier(
+ error, xhr,
+ S(gettext('Error retrieving properties - %s')).sprintf(
+ error.message || that.label).value(), function() {
console.warn(arguments);
});
- }
- },
+ }
+ // show failed message.
+ $msgContainer.text(gettext('Failed to retrieve data from the server.'));
});
var onDrop = function(type) {
@@ -359,6 +410,14 @@ define([
return;
}.bind(that);
},
+ __loadMoreRows: function(e) {
+ let elem = e.currentTarget;
+ if ((elem.scrollHeight - 10) < elem.scrollTop + elem.offsetHeight) {
+ if (this.data.length > 0) {
+ this.collection.add(this.data.splice(0, 50));
+ }
+ }
+ },
generate_url: function(item, type) {
/*
* Using list, and collection functions of a node to get the nodes
diff --git a/web/pgadmin/misc/depends/__init__.py b/web/pgadmin/misc/dependencies/__init__.py
similarity index 84%
rename from web/pgadmin/misc/depends/__init__.py
rename to web/pgadmin/misc/dependencies/__init__.py
index 3ae8abce..e08f93e1 100644
--- a/web/pgadmin/misc/depends/__init__.py
+++ b/web/pgadmin/misc/dependencies/__init__.py
@@ -15,14 +15,14 @@ from pgadmin.utils import PgAdminModule
MODULE_NAME = 'depends'
-class DependsModule(PgAdminModule):
+class DependenciesModule(PgAdminModule):
def get_own_javascripts(self):
return [{
'name': 'pgadmin.browser.object_depends',
- 'path': url_for('depends.static', filename='js/depends'),
+ 'path': url_for('depends.static', filename='js/dependencies'),
'when': None
}]
# Initialise the module
-blueprint = DependsModule(MODULE_NAME, __name__, url_prefix='/misc/depends')
+blueprint = DependenciesModule(MODULE_NAME, __name__, url_prefix='/misc/depends')
diff --git a/web/pgadmin/misc/dependencies/static/js/dependencies.js b/web/pgadmin/misc/dependencies/static/js/dependencies.js
new file mode 100644
index 00000000..41e13da3
--- /dev/null
+++ b/web/pgadmin/misc/dependencies/static/js/dependencies.js
@@ -0,0 +1,245 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2019, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+define('misc.dependencies', [
+ 'sources/gettext', 'underscore', 'underscore.string', 'jquery', 'backbone',
+ 'pgadmin.browser', 'pgadmin.alertifyjs', 'pgadmin.backgrid',
+], function(gettext, _, S, $, Backbone, pgBrowser, Alertify, Backgrid) {
+
+ if (pgBrowser.NodeDependencies)
+ return pgBrowser.NodeDependencies;
+
+ var wcDocker = window.wcDocker;
+
+ pgBrowser.NodeDependencies = pgBrowser.NodeDependencies || {};
+
+ _.extend(pgBrowser.NodeDependencies, {
+ init: function() {
+ if (this.initialized) {
+ return;
+ }
+
+ this.initialized = true;
+ this.dependenciesPanel = pgBrowser.docker.findPanels('dependencies')[0];
+ /* Parameter is used to set the proper label of the
+ * backgrid header cell.
+ */
+ _.bindAll(this, 'showDependencies', '__loadMoreRows', '__appendGridToPanel');
+
+ // Defining Backbone Model for Dependencies.
+ var Model = Backbone.Model.extend({
+ defaults: {
+ icon: 'icon-unknown',
+ type: undefined,
+ name: undefined,
+ /* field contains 'Database Name' for 'Tablespace and Role node',
+ * for other node it contains 'Restriction'.
+ */
+ field: undefined,
+ },
+ // This function is used to fetch/set the icon for the type(Function, Role, Database, ....)
+ parse: function(res) {
+ var node = pgBrowser.Nodes[res.type];
+ res.icon = node ? (_.isFunction(node['node_image']) ?
+ (node['node_image']).apply(node, [null, null]) :
+ (node['node_image'] || ('icon-' + res.type))) :
+ ('icon-' + res.type);
+ res.type = S.titleize(res.type.replace(/_/g, ' '), true);
+ return res;
+ },
+ });
+
+ // Defining Backbone Collection for Dependencies.
+ this.dependenciesCollection = new(Backbone.Collection.extend({
+ model: Model,
+ }))(null);
+
+ pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependencies);
+ this.__appendGridToPanel();
+ },
+
+ /* Function is used to create and render backgrid with
+ * empty collection. We just want to add backgrid into the
+ * panel only once.
+ */
+ __appendGridToPanel: function() {
+ var $container = this.dependenciesPanel.layout().scene().find('.pg-panel-content'),
+ $gridContainer = $container.find('.pg-panel-dependencies-container'),
+ grid = new Backgrid.Grid({
+ emptyText: 'No data found',
+ columns: [{
+ name: 'type',
+ label: gettext('Type'),
+ // Extend it to render the icon as per the type.
+ cell: Backgrid.Cell.extend({
+ render: function() {
+ Backgrid.Cell.prototype.render.apply(this, arguments);
+ this.$el.prepend($('', {
+ class: 'wcTabIcon ' + this.model.get('icon'),
+ }));
+ return this;
+ },
+ }),
+ editable: false,
+ },
+ {
+ name: 'name',
+ label: gettext('Name'),
+ cell: 'string',
+ editable: false,
+ },
+ {
+ name: 'field',
+ label: '', // label kept blank, it will change dynamically
+ cell: 'string',
+ editable: false,
+ },
+ ],
+
+ collection: this.dependenciesCollection,
+ className: 'backgrid table presentation table-bordered table-noouter-border table-hover',
+ });
+
+ // Condition is used to save grid object to change the label of the header.
+ this.dependenciesGrid = grid;
+
+ $gridContainer.append(grid.render().el);
+
+ return true;
+ },
+
+ // Fetch the actual data and update the collection
+ showDependencies: function(item, data, node) {
+ let self = this,
+ msg = gettext('Please select an object in the tree view.'),
+ panel = this.dependenciesPanel,
+ $container = panel.layout().scene().find('.pg-panel-content'),
+ $msgContainer = $container.find('.pg-panel-depends-message'),
+ $gridContainer = $container.find('.pg-panel-dependencies-container'),
+ treeHierarchy = node.getTreeNodeHierarchy(item),
+ n_type = data._type,
+ url = node.generate_url(item, 'dependency', data, true);
+
+ if (node) {
+ /* We fetch the Dependencies and Dependencies tab only for
+ * those node who set the parameter hasDepends to true.
+ */
+ msg = gettext('No dependencies information is available for the current object.');
+ if (node.hasDepends) {
+ /* Updating the label for the 'field' type of the backbone model.
+ * Label should be "Database" if the node type is tablespace or role
+ * and dependencies tab is selected. For other nodes and dependencies tab
+ * it should be 'Restriction'.
+ */
+
+ this.dependenciesGrid.columns.models[2].set({
+ 'label': gettext('Restriction'),
+ });
+
+ // Hide message container and show grid container.
+ $msgContainer.addClass('d-none');
+ $gridContainer.removeClass('d-none');
+
+ var timer = '';
+ $.ajax({
+ url: url,
+ type: 'GET',
+ beforeSend: function() {
+ // Generate a timer for the request
+ timer = setTimeout(function() {
+ // notify user if request is taking longer than 1 second
+
+ $msgContainer.text(gettext('Fetching dependencies information from the server...'));
+ $msgContainer.removeClass('d-none');
+ msg = '';
+
+ }, 1000);
+ },
+ })
+ .done(function(res) {
+ clearTimeout(timer);
+
+ if (res.length > 0) {
+
+ if (!$msgContainer.hasClass('d-none')) {
+ $msgContainer.addClass('d-none');
+ }
+ $gridContainer.removeClass('d-none');
+
+ self.dependenciesData = res;
+ // Load only 50 rows
+ self.dependenciesCollection.reset(self.dependenciesData.splice(0, 50));
+
+ // Load more rows on scroll down
+ pgBrowser.Events.on(
+ 'pgadmin-browser:panel-dependencies:' +
+ wcDocker.EVENT.SCROLLED,
+ self.__loadMoreRows
+ );
+
+ } else {
+ // Do not listen the scroll event
+ pgBrowser.Events.off(
+ 'pgadmin-browser:panel-dependencies:' +
+ wcDocker.EVENT.SCROLLED
+ );
+
+ self.dependenciesCollection.reset({silent: true});
+ $msgContainer.text(msg);
+ $msgContainer.removeClass('d-none');
+
+ if (!$gridContainer.hasClass('d-none')) {
+ $gridContainer.addClass('d-none');
+ }
+ }
+
+
+ })
+ .fail(function(xhr, error, message) {
+ var _label = treeHierarchy[n_type].label;
+ pgBrowser.Events.trigger(
+ 'pgadmin:node:retrieval:error', 'depends', xhr, error, message
+ );
+ if (!Alertify.pgHandleItemError(xhr, error, message, {
+ item: item,
+ info: treeHierarchy,
+ })) {
+ Alertify.pgNotifier(
+ error, xhr,
+ S(gettext('Error retrieving the information - %s')).sprintf(
+ message || _label).value(), function() {
+ console.warn(arguments);
+ });
+ }
+ // show failed message.
+ $msgContainer.text(gettext('Failed to retrieve data from the server.'));
+ });
+ }
+ } if (msg != '') {
+ $msgContainer.text(msg);
+ $msgContainer.removeClass('d-none');
+ if (!$gridContainer.hasClass('d-none')) {
+ $gridContainer.addClass('d-none');
+ }
+ }
+ },
+ __loadMoreRows: function() {
+ if (this.dependenciesPanel.length < 1) return ;
+
+ let elem = this.dependenciesPanel.$container.find('.pg-panel-dependencies-container').closest('.wcFrameCenter')[0];
+ if ((elem.scrollHeight - 10) < elem.scrollTop + elem.offsetHeight) {
+ if (this.dependenciesData.length > 0) {
+ this.dependenciesCollection.add(this.dependenciesData.splice(0, 50));
+ }
+ }
+ },
+ });
+
+ return pgBrowser.NodeDependencies;
+});
diff --git a/web/pgadmin/misc/dependents/__init__.py b/web/pgadmin/misc/dependents/__init__.py
new file mode 100644
index 00000000..878d865e
--- /dev/null
+++ b/web/pgadmin/misc/dependents/__init__.py
@@ -0,0 +1,28 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2019, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""A blueprint module providing utility functions for the application."""
+
+from flask import url_for
+from pgadmin.utils import PgAdminModule
+
+MODULE_NAME = 'dependents'
+
+
+class DependentsModule(PgAdminModule):
+ def get_own_javascripts(self):
+ return [{
+ 'name': 'pgadmin.browser.dependents',
+ 'path': url_for('dependents.static', filename='js/dependents'),
+ 'when': None
+ }]
+
+
+# Initialise the module
+blueprint = DependentsModule(MODULE_NAME, __name__, url_prefix='/misc/dependents')
diff --git a/web/pgadmin/misc/dependents/static/js/dependents.js b/web/pgadmin/misc/dependents/static/js/dependents.js
new file mode 100644
index 00000000..b86e6b03
--- /dev/null
+++ b/web/pgadmin/misc/dependents/static/js/dependents.js
@@ -0,0 +1,251 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2019, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+define('misc.dependents', [
+ 'sources/gettext', 'underscore', 'underscore.string', 'jquery', 'backbone',
+ 'pgadmin.browser', 'pgadmin.alertifyjs', 'pgadmin.backgrid',
+], function(gettext, _, S, $, Backbone, pgBrowser, Alertify, Backgrid) {
+
+ if (pgBrowser.NodeDependents)
+ return pgBrowser.NodeDependents;
+
+ var wcDocker = window.wcDocker;
+
+ pgBrowser.NodeDependents = pgBrowser.NodeDependents || {};
+
+ _.extend(pgBrowser.NodeDependents, {
+ init: function() {
+ if (this.initialized) {
+ return;
+ }
+
+ this.initialized = true;
+ this.dependentsPanel = pgBrowser.docker.findPanels('dependents')[0];
+ /* Parameter is used to set the proper label of the
+ * backgrid header cell.
+ */
+ _.bindAll(this, 'showDependents', '__loadMoreRows', '__appendGridToPanel');
+
+ // Defining Backbone Model for Dependents.
+ var Model = Backbone.Model.extend({
+ defaults: {
+ icon: 'icon-unknown',
+ type: undefined,
+ name: undefined,
+ /* field contains 'Database Name' for 'Tablespace and Role node',
+ * for other node it contains 'Restriction'.
+ */
+ field: undefined,
+ },
+ // This function is used to fetch/set the icon for the type(Function, Role, Database, ....)
+ parse: function(res) {
+ var node = pgBrowser.Nodes[res.type];
+ res.icon = node ? (_.isFunction(node['node_image']) ?
+ (node['node_image']).apply(node, [null, null]) :
+ (node['node_image'] || ('icon-' + res.type))) :
+ ('icon-' + res.type);
+ res.type = S.titleize(res.type.replace(/_/g, ' '), true);
+ return res;
+ },
+ });
+
+ // Defining Backbone Collection for Dependents.
+ this.dependentCollection = new(Backbone.Collection.extend({
+ model: Model,
+ }))(null);
+
+ pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependents);
+ pgBrowser.Events.on('pgadmin-browser:tree:refreshing', this.refreshDependents, this);
+ this.__appendGridToPanel();
+ },
+
+ /* Function is used to create and render backgrid with
+ * empty collection. We just want to add backgrid into the
+ * panel only once.
+ */
+ __appendGridToPanel: function() {
+ var $container = this.dependentsPanel.layout().scene().find('.pg-panel-content'),
+ $gridContainer = $container.find('.pg-panel-dependents-container'),
+ grid = new Backgrid.Grid({
+ emptyText: 'No data found',
+ columns: [{
+ name: 'type',
+ label: gettext('Type'),
+ // Extend it to render the icon as per the type.
+ cell: Backgrid.Cell.extend({
+ render: function() {
+ Backgrid.Cell.prototype.render.apply(this, arguments);
+ this.$el.prepend($('', {
+ class: 'wcTabIcon ' + this.model.get('icon'),
+ }));
+ return this;
+ },
+ }),
+ editable: false,
+ },
+ {
+ name: 'name',
+ label: gettext('Name'),
+ cell: 'string',
+ editable: false,
+ },
+ {
+ name: 'field',
+ label: '', // label kept blank, it will change dynamically
+ cell: 'string',
+ editable: false,
+ },
+ ],
+
+ collection: this.dependentCollection,
+ className: 'backgrid table presentation table-bordered table-noouter-border table-hover',
+ });
+
+ // Condition is used to save grid object to change the label of the header.
+ this.dependentGrid = grid;
+
+ $gridContainer.append(grid.render().el);
+
+ return true;
+ },
+
+ // Fetch the actual data and update the collection
+ showDependents: function(item, data, node) {
+ let self = this,
+ msg = gettext('Please select an object in the tree view.'),
+ panel = this.dependentsPanel,
+ $container = panel.layout().scene().find('.pg-panel-content'),
+ $msgContainer = $container.find('.pg-panel-depends-message'),
+ $gridContainer = $container.find('.pg-panel-dependents-container'),
+ treeHierarchy = node.getTreeNodeHierarchy(item),
+ n_type = data._type,
+ url = node.generate_url(item, 'dependent', data, true);
+
+ if (node) {
+ /* We fetch the Dependencies and Dependents tab only for
+ * those node who set the parameter hasDepends to true.
+ */
+ msg = gettext('No dependent information is available for the current object.');
+ if (node.hasDepends) {
+ /* Updating the label for the 'field' type of the backbone model.
+ * Label should be "Database" if the node type is tablespace or role
+ * and dependent tab is selected. For other nodes and dependencies tab
+ * it should be 'Restriction'.
+ */
+ if (node.type == 'tablespace' || node.type == 'role') {
+ this.dependentGrid.columns.models[2].set({
+ 'label': gettext('Database'),
+ });
+ } else {
+ this.dependentGrid.columns.models[2].set({
+ 'label': gettext('Restriction'),
+ });
+ }
+
+ // Hide message container and show grid container.
+ $msgContainer.addClass('d-none');
+ $gridContainer.removeClass('d-none');
+
+ var timer = '';
+ $.ajax({
+ url: url,
+ type: 'GET',
+ beforeSend: function() {
+ // Generate a timer for the request
+ timer = setTimeout(function() {
+ // notify user if request is taking longer than 1 second
+
+ $msgContainer.text(gettext('Fetching dependent information from the server...'));
+ $msgContainer.removeClass('d-none');
+ msg = '';
+
+ }, 1000);
+ },
+ })
+ .done(function(res) {
+ clearTimeout(timer);
+
+ if (res.length > 0) {
+
+ if (!$msgContainer.hasClass('d-none')) {
+ $msgContainer.addClass('d-none');
+ }
+ $gridContainer.removeClass('d-none');
+
+ self.dependentData = res;
+ // Load only 50 rows
+ self.dependentCollection.reset(self.dependentData.splice(0, 50));
+
+ // Load more rows on scroll down
+ pgBrowser.Events.on(
+ 'pgadmin-browser:panel-dependents:' +
+ wcDocker.EVENT.SCROLLED,
+ self.__loadMoreRows
+ );
+
+ } else {
+ // Do not listen the scroll event
+ pgBrowser.Events.off(
+ 'pgadmin-browser:panel-dependents:' +
+ wcDocker.EVENT.SCROLLED
+ );
+
+ self.dependentCollection.reset({silent: true});
+ $msgContainer.text(msg);
+ $msgContainer.removeClass('d-none');
+
+ if (!$gridContainer.hasClass('d-none')) {
+ $gridContainer.addClass('d-none');
+ }
+ }
+
+
+ })
+ .fail(function(xhr, error, message) {
+ var _label = treeHierarchy[n_type].label;
+ pgBrowser.Events.trigger(
+ 'pgadmin:node:retrieval:error', 'depends', xhr, error, message
+ );
+ if (!Alertify.pgHandleItemError(xhr, error, message, {
+ item: item,
+ info: treeHierarchy,
+ })) {
+ Alertify.pgNotifier(
+ error, xhr,
+ S(gettext('Error retrieving the information - %s')).sprintf(
+ message || _label).value(), function() {
+ console.warn(arguments);
+ });
+ }
+ // show failed message.
+ $msgContainer.text(gettext('Failed to retrieve data from the server.'));
+ });
+ }
+ } if (msg != '') {
+ $msgContainer.text(msg);
+ $msgContainer.removeClass('d-none');
+ if (!$gridContainer.hasClass('d-none')) {
+ $gridContainer.addClass('d-none');
+ }
+ }
+ },
+ __loadMoreRows: function() {
+ if (this.dependentsPanel.length < 1) return ;
+
+ let elem = this.dependentsPanel.$container.find('.pg-panel-dependents-container').closest('.wcFrameCenter')[0];
+ if ((elem.scrollHeight - 10) < elem.scrollTop + elem.offsetHeight) {
+ if (this.dependentData.length > 0) {
+ this.dependentCollection.add(this.dependentData.splice(0, 50));
+ }
+ }
+ },
+ });
+
+ return pgBrowser.NodeDependents;
+});
diff --git a/web/pgadmin/misc/depends/static/js/depends.js b/web/pgadmin/misc/depends/static/js/depends.js
deleted file mode 100644
index 050b9c56..00000000
--- a/web/pgadmin/misc/depends/static/js/depends.js
+++ /dev/null
@@ -1,432 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2019, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-define('misc.depends', [
- 'sources/gettext', 'underscore', 'underscore.string', 'jquery', 'backbone',
- 'pgadmin.browser', 'pgadmin.alertifyjs', 'pgadmin.backgrid',
-], function(gettext, _, S, $, Backbone, pgBrowser, Alertify, Backgrid) {
-
- if (pgBrowser.ShowNodeDepends)
- return pgBrowser.ShowNodeDepends;
-
- var wcDocker = window.wcDocker;
-
- pgBrowser.ShowNodeDepends = pgBrowser.ShowNodeDepends || {};
-
- _.extend(pgBrowser.ShowNodeDepends, {
- init: function() {
- if (this.initialized) {
- return;
- }
-
- this.initialized = true;
- /* Parameter is used to set the proper label of the
- * backgrid header cell.
- */
- _.bindAll(this, 'showDependents', 'dependentsPanelVisibilityChanged',
- 'showDependencies', 'dependenciesPanelVisibilityChanged', '__updateCollection'
- );
-
- // We will listened to the visibility change of the Dependencies and Dependents panel
- pgBrowser.Events.on('pgadmin-browser:panel-dependencies:' + wcDocker.EVENT.VISIBILITY_CHANGED,
- this.dependenciesPanelVisibilityChanged);
- pgBrowser.Events.on('pgadmin-browser:panel-dependents:' + wcDocker.EVENT.VISIBILITY_CHANGED,
- this.dependentsPanelVisibilityChanged);
-
- // Defining Backbone Model for Dependencies and Dependents.
- var Model = Backbone.Model.extend({
- defaults: {
- icon: 'icon-unknown',
- type: undefined,
- name: undefined,
- /* field contains 'Database Name' for 'Tablespace and Role node',
- * for other node it contains 'Restriction'.
- */
- field: undefined,
- },
- // This function is used to fetch/set the icon for the type(Function, Role, Database, ....)
- parse: function(res) {
- var node = pgBrowser.Nodes[res.type];
- res.icon = node ? (_.isFunction(node['node_image']) ?
- (node['node_image']).apply(node, [null, null]) :
- (node['node_image'] || ('icon-' + res.type))) :
- ('icon-' + res.type);
- res.type = S.titleize(res.type.replace(/_/g, ' '), true);
- return res;
- },
- });
-
- // Defining Backbone Collection for Dependents.
- this.dependentCollection = new(Backbone.Collection.extend({
- model: Model,
- }))(null);
-
- // Defining Backbone Collection for Dependencies.
- this.dependenciesCollection = new(Backbone.Collection.extend({
- model: Model,
- }))(null);
-
- var self = this;
-
- /* Function is used to create and render backgrid with
- * empty collection. We just want to add backgrid into the
- * panel only once.
- */
- var appendGridToPanel = function(collection, panel, is_dependent) {
- var $container = panel[0].layout().scene().find('.pg-panel-content'),
- $gridContainer = $container.find('.pg-panel-depends-container'),
- grid = new Backgrid.Grid({
- emptyText: 'No data found',
- columns: [{
- name: 'type',
- label: gettext('Type'),
- // Extend it to render the icon as per the type.
- cell: Backgrid.Cell.extend({
- render: function() {
- Backgrid.Cell.prototype.render.apply(this, arguments);
- this.$el.prepend($('', {
- class: 'wcTabIcon ' + this.model.get('icon'),
- }));
- return this;
- },
- }),
- editable: false,
- },
- {
- name: 'name',
- label: gettext('Name'),
- cell: 'string',
- editable: false,
- },
- {
- name: 'field',
- label: '', // label kept blank, it will change dynamically
- cell: 'string',
- editable: false,
- },
- ],
-
- collection: collection,
- className: 'backgrid table presentation table-bordered table-noouter-border table-hover',
- });
-
- // Condition is used to save grid object to change the label of the header.
- if (is_dependent)
- self.dependentGrid = grid;
- else
- self.dependenciesGrid = grid;
-
- $gridContainer.append(grid.render().el);
-
- return true;
- };
-
- // We will listened to the visibility change of the Dependencies and Dependents panel
- pgBrowser.Events.on('pgadmin-browser:panel-dependencies:' + wcDocker.EVENT.VISIBILITY_CHANGED,
- this.dependenciesPanelVisibilityChanged);
- pgBrowser.Events.on('pgadmin-browser:panel-dependents:' + wcDocker.EVENT.VISIBILITY_CHANGED,
- this.dependentsPanelVisibilityChanged);
- pgBrowser.Events.on(
- 'pgadmin:browser:node:updated',
- function() {
- if (this.dependenciesPanels && this.dependenciesPanels.length) {
- $(this.dependenciesPanels[0]).data('node-prop', '');
- this.dependenciesPanelVisibilityChanged(this.dependenciesPanels[0]);
- }
- if (this.dependentsPanels && this.dependentsPanels.length) {
- $(this.dependentsPanels[0]).data('node-prop', '');
- this.dependentsPanelVisibilityChanged(this.dependentsPanels[0]);
- }
- }, this
- );
-
- // We will render the grid objects in the panel after some time, because -
- // it is possible, it is not yet available.
- // Find the panels to render the grid.
- var dependenciesPanels = this.dependenciesPanels = pgBrowser.docker.findPanels('dependencies');
- var dependentsPanels = this.dependentsPanels = pgBrowser.docker.findPanels('dependents');
-
- if (dependenciesPanels.length == 0) {
- pgBrowser.Events.on(
- 'pgadmin-browser:panel-dependencies:' + wcDocker.EVENT.INIT,
- function() {
- this.dependenciesPanels = pgBrowser.docker.findPanels('dependencies');
- appendGridToPanel(this.dependenciesCollection, this.dependenciesPanels, false);
-
- // If Dependencies panel exists and is focused then we need to listen the browser tree selection events.
- if ((dependenciesPanels[0].isVisible()) || dependenciesPanels.length != 1) {
- pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependencies);
- pgBrowser.Events.on('pgadmin-browser:tree:refreshing', this.refreshDependencies, this);
- }
- }.bind(this)
- );
- } else {
- appendGridToPanel(this.dependenciesCollection, this.dependenciesPanels, false);
-
- // If Dependencies panel exists and is focused then we need to listen the browser tree selection events.
- if ((dependenciesPanels[0].isVisible()) || dependenciesPanels.length != 1) {
- pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependencies);
- pgBrowser.Events.on('pgadmin-browser:tree:refreshing', this.refreshDependencies, this);
- }
- }
-
- if (dependentsPanels.length == 0) {
- pgBrowser.Events.on(
- 'pgadmin-browser:panel-dependents:' + wcDocker.EVENT.INIT,
- function() {
- this.dependentsPanels = pgBrowser.docker.findPanels('dependents');
- appendGridToPanel(this.dependentCollection, this.dependentsPanels, true);
-
- // If Dependents panel exists and is focused then we need to listen the browser tree selection events.
- if ((dependentsPanels[0].isVisible()) || dependentsPanels.length != 1) {
- pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependents);
- pgBrowser.Events.on('pgadmin-browser:tree:refreshing', this.refreshDependents, this);
- }
- }.bind(this)
- );
- } else {
- appendGridToPanel(this.dependentCollection, this.dependentsPanels, true);
-
- // If Dependents panel exists and is focused then we need to listen the browser tree selection events.
- if ((dependentsPanels[0].isVisible()) || dependentsPanels.length != 1) {
- pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showDependents);
- pgBrowser.Events.on('pgadmin-browser:tree:refreshing', this.refreshDependents, this);
- }
- }
- },
-
- // Fetch the actual data and update the collection
- __updateCollection: function(collection, panel, url, messages, node, item, type) {
- var msg = messages[0],
- $container = panel[0].layout().scene().find('.pg-panel-content'),
- $msgContainer = $container.find('.pg-panel-depends-message'),
- $gridContainer = $container.find('.pg-panel-depends-container'),
- treeHierarchy = node.getTreeNodeHierarchy(item),
- n_type = type,
- cache_flag = {
- node_type: n_type,
- url: url,
- };
-
- // Avoid unnecessary reloads
- if (_.isEqual($(panel[0]).data('node-prop'), cache_flag)) {
- return;
- }
-
- // Cache the current IDs for next time
- $(panel[0]).data('node-prop', cache_flag);
-
- // Hide the grid container and show the default message container
- if (!$gridContainer.hasClass('d-none'))
- $gridContainer.addClass('d-none');
- $msgContainer.removeClass('d-none');
-
- if (node) {
- msg = messages[1];
- /* We fetch the Dependencies and Dependents tab only for
- * those node who set the parameter hasDepends to true.
- */
- if (node.hasDepends) {
- /* Set the message because ajax request may take time to
- * fetch the information from the server.
- */
- msg = messages[2];
- $msgContainer.text(msg);
-
- /* Updating the label for the 'field' type of the backbone model.
- * Label should be "Database" if the node type is tablespace or role
- * and dependent tab is selected. For other nodes and dependencies tab
- * it should be 'Restriction'.
- */
- if (this.dependent && (node.type == 'tablespace' || node.type == 'role'))
- this.dependentGrid.columns.models[2].set({
- 'label': gettext('Database'),
- });
- else {
- this.dependenciesGrid.columns.models[2].set({
- 'label': gettext('Restriction'),
- });
- this.dependentGrid.columns.models[2].set({
- 'label': gettext('Restriction'),
- });
- }
-
- // Hide message container and show grid container.
- $msgContainer.addClass('d-none');
- $gridContainer.removeClass('d-none');
-
- var timer = setTimeout(function() {
- // notify user if request is taking longer than 1 second
-
- $msgContainer.text(gettext('Retrieving data from the server...'));
- $msgContainer.removeClass('d-none');
- if ($gridContainer) {
- $gridContainer.addClass('d-none');
- }
- }, 1000);
-
- // Set the url, fetch the data and update the collection
- collection.url = url;
- collection.fetch({
- reset: true,
- success: function() {
- clearTimeout(timer);
- $gridContainer.removeClass('d-none');
- if (!$msgContainer.hasClass('d-none')) {
- $msgContainer.addClass('d-none');
- }
- },
- error: function(coll, xhr, error, message) {
- var _label = treeHierarchy[n_type].label;
- pgBrowser.Events.trigger(
- 'pgadmin:node:retrieval:error', 'depends', xhr, error, message
- );
- if (!Alertify.pgHandleItemError(xhr, error, message, {
- item: item,
- info: treeHierarchy,
- })) {
- Alertify.pgNotifier(
- error, xhr,
- S(
- gettext('Error retrieving the information - %s')
- ).sprintf(message || _label).value(),
- function() {
- console.warn(arguments);
- }
- );
- }
- // show failed message.
- $msgContainer.text(gettext('Failed to retrieve data from the server.'));
- },
- });
- }
- }
- if (msg != '') {
- $msgContainer.text(msg);
- }
- },
- refreshDependents: function(item, data, node) {
- var that = this,
- cache_flag = {
- node_type: data._type,
- url: node.generate_url(item, 'dependent', data, true),
- };
-
- if (_.isEqual($(that.dependentsPanels[0]).data('node-prop'), cache_flag)) {
- // Reset the current item selection
- $(that.dependentsPanels[0]).data('node-prop', '');
- that.showDependents(item, data, node);
- }
- },
- showDependents: function(item, data, node) {
- /**
- * We can't start fetching the Dependents immediately, it is possible the user
- * is just using the keyboard to select the node, and just traversing
- * through. We will wait for some time before fetching the Dependents
- **/
- var self = this;
- if (!node) {
- return;
- }
- self.dependent = true;
- if (self.timeout) {
- clearTimeout(self.timeout);
- }
- self.timeout = setTimeout(
- self.__updateCollection(
- self.dependentCollection, self.dependentsPanels,
- node.generate_url(item, 'dependent', data, true), [
- gettext('No object selected.'),
- gettext('No dependent information is available for the current object.'),
- gettext('Fetching dependent information from the server...'),
- ], node, item, data._type
- ), 400
- );
- },
- dependentsPanelVisibilityChanged: function(panel) {
- if (panel.isVisible()) {
- var t = pgBrowser.tree,
- i = t.selected(),
- d = i && t.itemData(i),
- n = i && d && pgBrowser.Nodes[d._type];
-
- pgBrowser.ShowNodeDepends.showDependents.apply(pgBrowser.ShowNodeDepends, [i, d, n]);
-
- // We will start listening the tree selection event.
- pgBrowser.Events.on('pgadmin-browser:tree:selected', pgBrowser.ShowNodeDepends.showDependents);
- pgBrowser.Events.on('pgadmin-browser:tree:refreshing', pgBrowser.ShowNodeDepends.refreshDependents, this);
- } else {
-
- // We don't need to listen the tree item selection event.
- pgBrowser.Events.off('pgadmin-browser:tree:selected', pgBrowser.ShowNodeDepends.showDependents);
- pgBrowser.Events.off('pgadmin-browser:tree:refreshing', pgBrowser.ShowNodeDepends.refreshDependents, this);
- }
- },
- refreshDependencies: function(item, data, node) {
- var that = this,
- cache_flag = {
- node_type: data._type,
- url: node.generate_url(item, 'dependency', data, true),
- };
-
- if (_.isEqual($(that.dependenciesPanels[0]).data('node-prop'), cache_flag)) {
- // Reset the current item selection
- $(that.dependenciesPanels[0]).data('node-prop', '');
- that.showDependencies(item, data, node);
- }
- },
- showDependencies: function(item, data, node) {
- /**
- * We can't start fetching the Dependencies immediately, it is possible the user
- * is just using the keyboard to select the node, and just traversing
- * through. We will wait for some time before fetching the Dependencies
- **/
- var self = this;
- if (!node) {
- return;
- }
- self.dependent = false;
- if (self.timeout) {
- clearTimeout(self.timeout);
- }
- self.timeout = setTimeout(
- self.__updateCollection(
- self.dependenciesCollection,
- self.dependenciesPanels,
- node.generate_url(item, 'dependency', data, true), [gettext('Please select an object in the tree view.'), gettext('No dependency information is available for the current object.'),
- gettext('Fetching dependency information from the server...'),
- ],
- node,
- item,
- data._type
- ), 400
- );
- },
- dependenciesPanelVisibilityChanged: function(panel) {
- if (panel.isVisible()) {
- var t = pgBrowser.tree,
- i = t.selected(),
- d = i && t.itemData(i),
- n = i && d && pgBrowser.Nodes[d._type];
-
- pgBrowser.ShowNodeDepends.showDependencies.apply(pgBrowser.ShowNodeDepends, [i, d, n]);
-
- // We will start listening the tree selection event.
- pgBrowser.Events.on('pgadmin-browser:tree:selected', pgBrowser.ShowNodeDepends.showDependencies);
- pgBrowser.Events.on('pgadmin-browser:tree:refreshing', pgBrowser.ShowNodeDepends.refreshDependencies, this);
- } else {
- // We don't need to listen the tree item selection event.
- pgBrowser.Events.off('pgadmin-browser:tree:selected', pgBrowser.ShowNodeDepends.showDependencies);
- pgBrowser.Events.off('pgadmin-browser:tree:refreshing', pgBrowser.ShowNodeDepends.refreshDependencies, this);
- }
- },
- });
-
- return pgBrowser.ShowNodeDepends;
-});
diff --git a/web/webpack.config.js b/web/webpack.config.js
index 10ae9969..77bc0fcc 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -235,7 +235,8 @@ module.exports = {
'pgadmin.dashboard' +
',pgadmin.tools.user_management' +
',pgadmin.browser.object_statistics' +
- ',pgadmin.browser.object_depends' +
+ ',pgadmin.browser.dependencies' +
+ ',pgadmin.browser.dependents' +
',pgadmin.browser.object_sql' +
',pgadmin.browser.bgprocess' +
',pgadmin.node.server_group' +
diff --git a/web/webpack.shim.js b/web/webpack.shim.js
index be244949..b9645625 100644
--- a/web/webpack.shim.js
+++ b/web/webpack.shim.js
@@ -195,7 +195,8 @@ var webpackShimConfig = {
'pgadmin.browser.messages': '/browser/js/messages',
'pgadmin.browser.node': path.join(__dirname, './pgadmin/browser/static/js/node'),
'pgadmin.browser.node.ui': path.join(__dirname, './pgadmin/browser/static/js/node.ui'),
- 'pgadmin.browser.object_depends': path.join(__dirname, './pgadmin/misc/depends/static/js/depends'),
+ 'pgadmin.browser.dependencies': path.join(__dirname, './pgadmin/misc/dependencies/static/js/dependencies'),
+ 'pgadmin.browser.dependents': path.join(__dirname, './pgadmin/misc/dependents/static/js/dependents'),
'pgadmin.browser.object_sql': path.join(__dirname, './pgadmin/misc/sql/static/js/sql'),
'pgadmin.browser.object_statistics': path.join(__dirname, './pgadmin/misc/statistics/static/js/statistics'),
'pgadmin.browser.panel': path.join(__dirname, './pgadmin/browser/static/js/panel'),