public inbox for [email protected]
help / color / mirror / Atom feedFrom: Ashesh Vashi <[email protected]>
To: Anthony Emengo <[email protected]>
Cc: Joao De Almeida Pereira <[email protected]>
Cc: Dave Page <[email protected]>
Cc: Akshay Joshi <[email protected]>
Cc: Murtuza Zabuawala <[email protected]>
Cc: pgadmin-hackers <[email protected]>
Cc: Khushboo Vashi <[email protected]>
Subject: Re: [pgadmin4][patch] Initial patch to decouple from ACI Tree
Date: Thu, 24 May 2018 12:29:16 +0530
Message-ID: <CAG7mmoxmwsd9GOtvAw-eT6=vT5JEx0VJYVf1H97Tk57khezOwA@mail.gmail.com> (raw)
In-Reply-To: <CAG8BBZNndOje9T-B=Jrxj0tqJbDYc489yYbkJkJx_V5LKzsM-w@mail.gmail.com>
References: <CAE+jja=Gdd032H7tpoZD2C0m2R7SnTZpHX_oPx2K2zGbaaW9yg@mail.gmail.com>
<CAFOhELcrZsRkwV4beOC7UT_HYvi9Rk=HFJ355g8Bj4bqyzMLog@mail.gmail.com>
<CAE+jjaneGOR_R7QvYnZt=khumRsyhnrzFRhpMG5-Q+rR9guLzQ@mail.gmail.com>
<CAKKotZROcoO_rtrrJUTvpfgSh1w4ig1ogQrkDrKSQHuKEL7CdQ@mail.gmail.com>
<CAE+jjakEZtn0ct128Xd+es5xCZ5eZLwoNZw6X0ue7oFB+a83oQ@mail.gmail.com>
<CAFOhELe_t_QGBbcYYm3Zrfwf3z9674PY5sy2QS46wHDTf-8DHA@mail.gmail.com>
<CAE+jja=u+W+FNaWGVs1KPZm1eYA_F=7ktomSbmsxOo-cjuOdBw@mail.gmail.com>
<CAE+jja=TZzMXOq4+t=dA+LfTGkW66urB37Tt9sEJ_UjAiymbbQ@mail.gmail.com>
<CAE+jjamSM_pBLNmYLn6-hWMiQvToceV2iPuME-eH5c+tgaYf7A@mail.gmail.com>
<CA+OCxowN15jg_DV4o+R+OSczyGHHccnJ1sPXE_XukYugaTKyyw@mail.gmail.com>
<CA+OCxozTMcEDKZEu9YCOsSBSed23Ka8hHiO5s6VF76KopbipNQ@mail.gmail.com>
<CAG7mmowrM3b8cTY+zcCAcVma8CKWHwXSF3=BUDcSFLpjH=jQBg@mail.gmail.com>
<CAE+jjanjQ295x0DgffT8OARRv8fge5KOPJpMCnLWe+j0jdqGJQ@mail.gmail.com>
<CAG7mmoym9838PVKdDD=469Bsx6AFBV1ThMK_dyitUmG26147Qg@mail.gmail.com>
<CAG7mmoy=5W3scnPs-TVpcu49F7gZU2qySPaakwfEdcg6rg2RMQ@mail.gmail.com>
<CAG8BBZOAFgw6iDseWTSEUC+tiwc0zCoQyycXGFWbW6ueftCggA@mail.gmail.com>
<CAG7mmoyirqdDGZnXcz5odMBnsOYY83Dj6Uw8gV59OPTJSnaMAg@mail.gmail.com>
<CA+OCxoz3C+HRDzh05SKhMjSZPnHezf=F0eiNoo_HHqUtiJWPiQ@mail.gmail.com>
<CAG7mmoxjfZGA8Gp4HMByTAf6ZLZ7Oaj2yTUrgbOrpJ0H7OtQNQ@mail.gmail.com>
<CAE+jja=GFVqodrWJ+vzBYO4tzoZipKqdThKUqMOGyoZPtJc1+Q@mail.gmail.com>
<CAG7mmozE+8pneLOp8RokHZiav0kcM2Ts6NkHwrz2n31-Ykn_kA@mail.gmail.com>
<CA+OCxowNhQ7waRSPp5P--dFisLFkf+EEOwH+y0+t+ZT9dXLXTA@mail.gmail.com>
<CAE+jjamkYzDxjvSmdkU-1Gyys1FXtrXGr+d7=yrsAREmXsteOQ@mail.gmail.com>
<CAE+jja=vaQOE6XbJH34+N24CZY1DRdYz8wgkxxTx5KSaqw7pLQ@mail.gmail.com>
<CAE+jjakrO-RaK=m5YYUBrF=9SgYjOyU59bZo+vWxDoFXG75Jvg@mail.gmail.com>
<CAE+jjanc7WbL6k42=Dp8NBw9mdiv4jccK7t5JfN2rD2n8NiYTg@mail.gmail.com>
<CAFOhELfyS404C50JWdgV16sBO7WYAJYYOD80kBA1AEhREALCmw@mail.gmail.com>
<CANxoLDfyOFt2QDAXE5BVC6X8tHZV4q3K=_gRiuWN_ife9FYU_w@mail.gmail.com>
<CAE+jjamBMWfWCypF_h5x0NThhmGHx7to6HsJ66WSmJrL6UdQ+g@mail.gmail.com>
<CAG7mmoyjWCTZUXH-CBTpgNy4ErA_6ojRgd+dT63zdAkGMY_2JQ@mail.gmail.com>
<CAG8BBZOKQDKtyu1-2HVCP5HFy1rKFdPVrMTY0SPEhb8x7Q7PBA@mail.gmail.com>
<CAG7mmowkNtkoLGQFeWAH-TSOi+QWbHT2-RbMmjRDAMAxMufmeg@mail.gmail.com>
<CAE+jja=PLKUdA43dAz2exgLcy6n5EGbM0E4K1ZdKmi5hex7Rew@mail.gmail.com>
<CAG7mmoyxBz+D+933L9UzgQrmRgn3xHxHuW06fwsNr-WkgXD_8w@mail.gmail.com>
<CA+OCxoz3eSNJ25sy_AmvERjNp24pD=Dfq-3a4ZQrmBVedDf-4g@mail.gmail.com>
<CAG7mmoxqA-Wa4hi0nwwg9nQKo1zmvYwfLrnGvVJjmobsNCCYmQ@mail.gmail.com>
<CA+OCxoz_V+z3WrfsbxXWNYcFHCqaURRwiJb6R_N1Yn0PWZcd8A@mail.gmail.com>
<CAG7mmozzeu=oRrYv+QYR3u6jWUfj7rZMzTbF23w3cB7WEkY7fA@mail.gmail.com>
<CAG7mmoxk6mzxxNm1YKU6=wKJ9KTAf4gKYX0y0AJxh6pS1h6HCw@mail.gmail.com>
<CAE+jjakrT2AhM2gEGt+QKEANMypn08O4vweOgwhyqQdV6mZEEQ@mail.gmail.com>
<CAG7mmowDFFbPFSxn0swpM2Ub_W0x842Yz=qiBmAC8ZxPYtn+Rw@mail.gmail.com>
<CAG8BBZNndOje9T-B=Jrxj0tqJbDYc489yYbkJkJx_V5LKzsM-w@mail.gmail.com>
Sorry for the late reply.
On Wed, May 16, 2018 at 8:55 PM, Anthony Emengo <[email protected]> wrote:
> export function canCreate(pgBrowser, childOfCatalogType) {
> return canCreateObject.bind({
> browser: pgBrowser,
> childOfCatalogType: childOfCatalogType,
> });
> }
>
> With respect to the above code: this bind pattern looks good and seems
> like the idiomatic way to handle this in JavaScript. On a lighter node, I
> don’t even see the need for an additional method to wrap it. The invocation
> could have easily been like canCreate: canCreateObject.bind({ browser:
> pgBrowser, childOfCatalogType: childOfCatalogType }), I don’t feel too
> strongly here.
>
I do agree - we can handle the same problem many ways.
I prefer object oriented pardigm more in general.
Any way - I have modified the code with some other changes.
> I renamed it as isValidTreeNodeData, because - we were using it in for
> testing the tree data. Please suggest me the right place, and name.
>
> We’re not sure; maybe after continued refactoring, we will come across
> more generic functions. At that point we can revisit this and create a
> utils.js file.
>
Sure.
> The original patch was separating them in different places, but - still
> uses some of the functionalities directly from the tree, which was
> happening because we have contextual menu.
> To give a better solution, I can think of putting the menus related code
> understand ‘sources/tree/menu’ directory.
>
> We’re particularly worried because we’re trying to avoid the coupling that
> we see in the code base today. We want to decouple *application state*
> from *business domain* logic as much as we can - because this makes the
> code much easier to understand. We achieve lower coupling by have more
> suitable interfaces to retrieve *application state* like: anyParent (the
> menu doesn’t care how this happens). This is the direction that we’re
> trying to move towards, we just don’t want the package structure to
> undermine that developer intent.
>
I realized after revisiting the code, menu/can_create.js was only
applicable to the children of the schema/catalog nodes, same as
'can_drop_child'.
We should have put both scripts in the same directory.
Please find the updated patch for the same.
Please review it, and let me know your concerns.
-- Thanks, Ashesh
> How about nodeMenu.isSupportedNode(…)?
>
> Naming is one of the hardest problems in programming. I don’t feel too
> strongly about this one. For now, let’s keep it as is
>
> Thanks
> Anthony && Victoria
>
>
>
>
>
Attachments:
[application/octet-stream] 0001-Extract-test-and-refactor-methods.patch (279.2K, 3-0001-Extract-test-and-refactor-methods.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js
index 9015d8d2..7fd28a7c 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js
@@ -1,8 +1,8 @@
define('pgadmin.node.collation', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
- 'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser) {
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, schemaChild) {
if (!pgBrowser.Nodes['coll-collation']) {
pgAdmin.Browser.Nodes['coll-collation'] =
@@ -15,7 +15,7 @@ define('pgadmin.node.collation', [
}
if (!pgBrowser.Nodes['collation']) {
- pgAdmin.Browser.Nodes['collation'] = pgBrowser.Node.extend({
+ pgAdmin.Browser.Nodes['collation'] = schemaChild.SchemaChildNode.extend({
type: 'collation',
sqlAlterHelp: 'sql-altercollation.html',
sqlCreateHelp: 'sql-createcollation.html',
@@ -222,34 +222,6 @@ define('pgadmin.node.collation', [
return true;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create collation
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-collation' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js
index 403ca471..a91daa5f 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js
@@ -2,9 +2,10 @@
define('pgadmin.node.domain', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
- 'pgadmin.browser.collection',
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
], function(
- gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid
+ gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid,
+ schemaChild
) {
// Define Domain Collection Node
@@ -79,7 +80,7 @@ define('pgadmin.node.domain', [
// Domain Node
if (!pgBrowser.Nodes['domain']) {
- pgBrowser.Nodes['domain'] = pgBrowser.Node.extend({
+ pgBrowser.Nodes['domain'] = schemaChild.SchemaChildNode.extend({
type: 'domain',
sqlAlterHelp: 'sql-alterdomain.html',
sqlCreateHelp: 'sql-createdomain.html',
@@ -88,7 +89,6 @@ define('pgadmin.node.domain', [
collection_type: 'coll-domain',
hasSQL: true,
hasDepends: true,
- parent_type: ['schema', 'catalog'],
Init: function() {
// Avoid mulitple registration of menus
if (this.initialized)
@@ -118,8 +118,6 @@ define('pgadmin.node.domain', [
]);
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
// Domain Node Model
model: pgBrowser.Node.Model.extend({
initialize: function(attrs, args) {
@@ -296,34 +294,6 @@ define('pgadmin.node.domain', [
return errmsg;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create domain
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-domain' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
isDisabled: function(m){
if (!m.isNew()) {
var server = this.node_info.server;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js
index 160db83f..24f5e1a7 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js
@@ -2,9 +2,10 @@
define('pgadmin.node.foreign_table', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
- 'pgadmin.browser.collection',
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
], function(
- gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid
+ gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid,
+ schemaChild
) {
if (!pgBrowser.Nodes['coll-foreign_table']) {
@@ -469,7 +470,7 @@ define('pgadmin.node.foreign_table', [
if (!pgBrowser.Nodes['foreign_table']) {
- pgBrowser.Nodes['foreign_table'] = pgBrowser.Node.extend({
+ pgBrowser.Nodes['foreign_table'] = schemaChild.SchemaChildNode.extend({
type: 'foreign_table',
sqlAlterHelp: 'sql-alterforeigntable.html',
sqlCreateHelp: 'sql-createforeigntable.html',
@@ -509,8 +510,6 @@ define('pgadmin.node.foreign_table', [
]);
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
model: pgBrowser.Node.Model.extend({
initialize: function(attrs, args) {
var isNew = (_.size(attrs) === 0);
@@ -659,34 +658,6 @@ define('pgadmin.node.foreign_table', [
return errmsg;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create foreign table
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-foreign_table' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js
index 89806681..cba62789 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js
@@ -1,9 +1,10 @@
define('pgadmin.node.fts_configuration', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
- 'pgadmin.browser.collection',
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
], function(
- gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid
+ gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid,
+ schemaChild
) {
// Model for tokens control
@@ -410,14 +411,11 @@ define('pgadmin.node.fts_configuration', [
// Extend the node class for FTS Configuration
if (!pgBrowser.Nodes['fts_configuration']) {
- pgAdmin.Browser.Nodes['fts_configuration'] = pgAdmin.Browser.Node.extend({
- parent_type: ['schema', 'catalog'],
+ pgAdmin.Browser.Nodes['fts_configuration'] = schemaChild.SchemaChildNode.extend({
type: 'fts_configuration',
sqlAlterHelp: 'sql-altertsconfig.html',
sqlCreateHelp: 'sql-createtsconfig.html',
dialogHelp: url_for('help.static', {'filename': 'fts_configuration_dialog.html'}),
- canDrop: true,
- canDropCascade: true,
label: gettext('FTS Configuration'),
hasSQL: true,
hasDepends: true,
@@ -577,34 +575,6 @@ define('pgadmin.node.fts_configuration', [
return null;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create fts configuration
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-fts_configuration' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js
index ed83feb1..cf733922 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js
@@ -1,8 +1,10 @@
define('pgadmin.node.fts_dictionary', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
- 'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform) {
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+], function(
+ gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, schemaChild
+) {
// Extend the browser's node model class to create a option/value pair
var OptionLabelModel = pgAdmin.Browser.Node.Model.extend({
@@ -55,14 +57,11 @@ define('pgadmin.node.fts_dictionary', [
// Extend the node class for FTS Dictionary
if (!pgBrowser.Nodes['fts_dictionary']) {
- pgAdmin.Browser.Nodes['fts_dictionary'] = pgAdmin.Browser.Node.extend({
- parent_type: ['schema', 'catalog'],
+ pgAdmin.Browser.Nodes['fts_dictionary'] = schemaChild.SchemaChildNode.extend({
type: 'fts_dictionary',
sqlAlterHelp: 'sql-altertsdictionary.html',
sqlCreateHelp: 'sql-createtsdictionary.html',
dialogHelp: url_for('help.static', {'filename': 'fts_dictionary_dialog.html'}),
- canDrop: true,
- canDropCascade: true,
label: gettext('FTS Dictionary'),
hasSQL: true,
hasDepends: true,
@@ -186,34 +185,6 @@ define('pgadmin.node.fts_dictionary', [
return null;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create fts dictionary
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-fts_dictionary' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js
index 92c0786e..bebc9dc5 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js
@@ -1,7 +1,8 @@
define('pgadmin.node.fts_parser', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
- 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser) {
+ 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.node.schema.dir/child',
+ 'pgadmin.browser.collection',
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, schemaChild) {
// Extend the collection class for fts parser
if (!pgBrowser.Nodes['coll-fts_parser']) {
@@ -16,14 +17,11 @@ define('pgadmin.node.fts_parser', [
// Extend the node class for fts parser
if (!pgBrowser.Nodes['fts_parser']) {
- pgAdmin.Browser.Nodes['fts_parser'] = pgAdmin.Browser.Node.extend({
- parent_type: ['schema', 'catalog'],
+ pgAdmin.Browser.Nodes['fts_parser'] = schemaChild.SchemaChildNode.extend({
type: 'fts_parser',
sqlAlterHelp: 'sql-altertsparser.html',
sqlCreateHelp: 'sql-createtsparser.html',
dialogHelp: url_for('help.static', {'filename': 'fts_parser_dialog.html'}),
- canDrop: true,
- canDropCascade: true,
label: gettext('FTS Parser'),
hasSQL: true,
hasDepends: true,
@@ -199,34 +197,6 @@ define('pgadmin.node.fts_parser', [
return null;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create fts parser
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-fts_parser' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js
index 606a57a6..cd0207ab 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js
@@ -1,7 +1,8 @@
define('pgadmin.node.fts_template', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
- 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser) {
+ 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.node.schema.dir/child',
+ 'pgadmin.browser.collection',
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, schemaChild) {
// Extend the collection class for fts template
if (!pgBrowser.Nodes['coll-fts_template']) {
@@ -16,14 +17,11 @@ define('pgadmin.node.fts_template', [
// Extend the node class for fts template
if (!pgBrowser.Nodes['fts_template']) {
- pgAdmin.Browser.Nodes['fts_template'] = pgAdmin.Browser.Node.extend({
- parent_type: ['schema', 'catalog'],
+ pgAdmin.Browser.Nodes['fts_template'] = schemaChild.SchemaChildNode.extend({
type: 'fts_template',
sqlAlterHelp: 'sql-altertstemplate.html',
sqlCreateHelp: 'sql-createtstemplate.html',
dialogHelp: url_for('help.static', {'filename': 'fts_template_dialog.html'}),
- canDrop: true,
- canDropCascade: true,
label: gettext('FTS Template'),
hasSQL: true,
hasDepends: true,
@@ -139,34 +137,6 @@ define('pgadmin.node.fts_template', [
return null;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create fts fts_template
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-fts_template' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
}
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 6e405165..c4cd91aa 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
@@ -2,8 +2,11 @@
define('pgadmin.node.function', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
- 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege',
-], function(gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform) {
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+ 'pgadmin.browser.server.privilege',
+], function(
+ gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, schemaChild
+) {
if (!pgBrowser.Nodes['coll-function']) {
pgBrowser.Nodes['coll-function'] =
@@ -83,7 +86,8 @@ define('pgadmin.node.function', [
});
if (!pgBrowser.Nodes['function']) {
- pgBrowser.Nodes['function'] = pgBrowser.Node.extend({
+
+ pgBrowser.Nodes['function'] = schemaChild.SchemaChildNode.extend({
type: 'function',
sqlAlterHelp: 'sql-alterfunction.html',
sqlCreateHelp: 'sql-createfunction.html',
@@ -96,7 +100,6 @@ define('pgadmin.node.function', [
return treeInformation.server.server_type !== 'gpdb';
},
hasScriptTypes: ['create', 'select'],
- parent_type: ['schema', 'catalog'],
Init: function() {
/* Avoid mulitple registration of menus */
if (this.initialized)
@@ -126,8 +129,6 @@ define('pgadmin.node.function', [
]);
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
model: pgBrowser.Node.Model.extend({
initialize: function(attrs, args) {
var isNew = (_.size(attrs) === 0);
@@ -438,34 +439,6 @@ define('pgadmin.node.function', [
return true;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create Function
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-function' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
}
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 aeb8271b..fcdf28fb 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
@@ -2,8 +2,11 @@
define('pgadmin.node.trigger_function', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
- 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform) {
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+ 'pgadmin.browser.server.privilege',
+], function(
+ gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, schemaChild
+) {
if (!pgBrowser.Nodes['coll-trigger_function']) {
pgBrowser.Nodes['coll-trigger_function'] =
@@ -17,7 +20,7 @@ define('pgadmin.node.trigger_function', [
}
if (!pgBrowser.Nodes['trigger_function']) {
- pgBrowser.Nodes['trigger_function'] = pgBrowser.Node.extend({
+ pgBrowser.Nodes['trigger_function'] = schemaChild.SchemaChildNode.extend({
type: 'trigger_function',
sqlAlterHelp: 'plpgsql-trigger.html',
sqlCreateHelp: 'plpgsql-trigger.html',
@@ -27,7 +30,6 @@ define('pgadmin.node.trigger_function', [
hasSQL: true,
hasDepends: true,
hasStatistics: true,
- parent_type: ['schema', 'catalog'],
Init: function() {
/* Avoid mulitple registration of menus */
if (this.initialized)
@@ -57,8 +59,6 @@ define('pgadmin.node.trigger_function', [
]);
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
model: pgBrowser.Node.Model.extend({
initialize: function(attrs, args) {
var isNew = (_.size(attrs) === 0);
@@ -357,34 +357,6 @@ define('pgadmin.node.trigger_function', [
return !(this.node_info && 'catalog' in this.node_info);
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create Function
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-trigger_function' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js
index 57c95acd..300f0b10 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js
@@ -1,8 +1,10 @@
define('pgadmin.node.sequence', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
- 'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform) {
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+], function(
+ gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, schemaChild
+) {
// Extend the browser's collection class for sequence collection
if (!pgBrowser.Nodes['coll-sequence']) {
@@ -18,7 +20,7 @@ define('pgadmin.node.sequence', [
// Extend the browser's node class for sequence node
if (!pgBrowser.Nodes['sequence']) {
- pgBrowser.Nodes['sequence'] = pgBrowser.Node.extend({
+ pgBrowser.Nodes['sequence'] = schemaChild.SchemaChildNode.extend({
type: 'sequence',
sqlAlterHelp: 'sql-altersequence.html',
sqlCreateHelp: 'sql-createsequence.html',
@@ -28,7 +30,6 @@ define('pgadmin.node.sequence', [
hasSQL: true,
hasDepends: true,
hasStatistics: true,
- parent_type: ['schema', 'catalog'],
Init: function() {
/* Avoid mulitple registration of menus */
if (this.initialized)
@@ -58,36 +59,6 @@ define('pgadmin.node.sequence', [
]);
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create collation
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-sequence' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we want to allow create menu
- return true;
- },
// Define the model for sequence node.
model: pgBrowser.Node.Model.extend({
defaults: {
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/child.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/child.js
new file mode 100644
index 00000000..f8e5951c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/child.js
@@ -0,0 +1,22 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+/////////////////////////////////////////////////////////////
+
+import * as Node from 'pgbrowser/node';
+import {
+ isTreeItemOfChildOfSchema, childCreateMenuEnabled,
+} from './schema_child_tree_node';
+
+let SchemaChildNode = Node.extend({
+ parent_type: ['schema', 'catalog'],
+ canDrop: isTreeItemOfChildOfSchema,
+ canDropCascade: isTreeItemOfChildOfSchema,
+ canCreate: childCreateMenuEnabled,
+}, false);
+
+export {SchemaChildNode};
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 a7fd4c7c..3b9b0f35 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
@@ -425,54 +425,10 @@ define('pgadmin.node.schema', [
return null;
},
}),
- // This function will checks whether we can allow user to
- // drop object or not based on location within schema & catalog
- canChildDrop: function(itemData, item) {
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create collation
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if(prev_d && prev_d._type == 'catalog') {
- return false;
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
pgBrowser.tableChildTreeNodeHierarchy = function(i) {
- var idx = 0,
- res = {},
- t = pgBrowser.tree;
-
- do {
- var d = t.itemData(i);
- if (
- d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId
- ) {
- if (d._type === 'partition' || d._type === 'table') {
- if (!('table' in res)) {
- res['table'] = _.extend({}, d, {'priority': idx});
- idx -= 1;
- }
- } else {
- res[d._type] = _.extend({}, d, {'priority': idx});
- idx -= 1;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- } while (i);
-
- return res;
+ return this.getTreeNodeHierarchy(i);
};
}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema_child_tree_node.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema_child_tree_node.js
new file mode 100644
index 00000000..1f67d1af
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema_child_tree_node.js
@@ -0,0 +1,41 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+/////////////////////////////////////////////////////////////
+
+import * as pgBrowser from 'pgbrowser/browser';
+
+export function childCreateMenuEnabled(itemData, item, data) {
+ // If check is false then , we will allow create menu
+ if (data && data.check === false) {
+ return true;
+ }
+
+ let node = pgBrowser.treeMenu.findNodeByDomElement(item);
+
+ if (node)
+ return node.anyFamilyMember(
+ (node) => (node.getData()._type === 'schema')
+ );
+
+ return false;
+}
+
+export function isTreeItemOfChildOfSchema(itemData, item) {
+ let node = pgBrowser.treeMenu.findNodeByDomElement(item);
+
+ if (node)
+ return isTreeNodeOfSchemaChild(node);
+
+ return false;
+}
+
+export function isTreeNodeOfSchemaChild(node) {
+ return node.anyParent(
+ (parentNode) => (parentNode.getData()._type === 'schema')
+ );
+}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
index 72b6eb0c..4dd804a6 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
@@ -88,7 +88,6 @@ define('pgadmin.node.column', [
if (!pgBrowser.Nodes['column']) {
pgBrowser.Nodes['column'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
parent_type: ['table', 'view', 'mview'],
collection_type: ['coll-table', 'coll-view', 'coll-mview'],
type: 'column',
@@ -97,27 +96,24 @@ define('pgadmin.node.column', [
sqlAlterHelp: 'sql-altertable.html',
sqlCreateHelp: 'sql-altertable.html',
dialogHelp: url_for('help.static', {'filename': 'column_dialog.html'}),
- canDrop: function(itemData, item, data){
- if (pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data])) {
- var t = pgBrowser.tree, i = item, d = itemData, parents = [];
- // To iterate over tree to check parent node
- while (i) {
- parents.push(d._type);
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
+ canDrop: function(itemData, item){
+ let node = pgBrowser.treeMenu.findNodeByDomElement(item);
- // Check if menu is allowed ?
- if(_.indexOf(parents, 'catalog') > -1 ||
- _.indexOf(parents, 'view') > -1 ||
- _.indexOf(parents, 'mview') > -1) {
- return false;
- } else if(_.indexOf(parents, 'table') > -1) {
- return true;
- }
- } else {
+ if (!node)
return false;
- }
+
+ // Only a column of a table can be droped, and only when it is not of
+ // catalog.
+ return node.anyParent(
+ (parentNode) => (
+ parentNode.getData()._type === 'table' &&
+ !parentNode.anyParent(
+ (grandParentNode) => (
+ grandParentNode.getData()._type === 'catalog'
+ )
+ )
+ )
+ );
},
hasDepends: true,
hasStatistics: true,
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js
index 857cf4c4..ab28a86b 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js
@@ -8,7 +8,6 @@ define('pgadmin.node.check_constraint', [
// Check Constraint Node
if (!pgBrowser.Nodes['check_constraint']) {
pgAdmin.Browser.Nodes['check_constraint'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
type: 'check_constraint',
label: gettext('Check'),
collection_type: 'coll-constraints',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js
index 0bbf66a1..adccf2e9 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js
@@ -605,7 +605,6 @@ define('pgadmin.node.exclusion_constraint', [
// Extend the browser's node class for exclusion constraint node
if (!pgBrowser.Nodes['exclusion_constraint']) {
pgAdmin.Browser.Nodes['exclusion_constraint'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
type: 'exclusion_constraint',
label: gettext('Exclusion constraint'),
collection_type: 'coll-constraints',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
index 3c4b89f3..9899df92 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
@@ -603,7 +603,6 @@ define('pgadmin.node.foreign_key', [
// Extend the browser's node class for foreign key node
if (!pgBrowser.Nodes['foreign_key']) {
pgAdmin.Browser.Nodes['foreign_key'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
type: 'foreign_key',
label: gettext('Foreign key'),
collection_type: 'coll-constraints',
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 d3a6cff4..0ad0f054 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
@@ -20,7 +20,6 @@ define('pgadmin.node.primary_key', [
parent_type: ['table','partition'],
canDrop: true,
canDropCascade: true,
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
Init: function() {
/* Avoid multiple registration of menus */
if (this.initialized)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js
index 769185d6..18d3ca33 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js
@@ -20,7 +20,6 @@ define('pgadmin.node.unique_constraint', [
parent_type: ['table','partition'],
canDrop: true,
canDropCascade: true,
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
Init: function() {
/* Avoid multiple registration of menus */
if (this.initialized)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js
index cb242cd2..9c0e24fe 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js
@@ -12,14 +12,12 @@ define('pgadmin.node.constraints', [
node: 'constraints',
label: gettext('Constraints'),
type: 'coll-constraints',
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
columns: ['name', 'comment'],
});
}
if (!pgBrowser.Nodes['constraints']) {
pgAdmin.Browser.Nodes['constraints'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
type: 'constraints',
label: gettext('Constraints'),
collection_type: 'coll-constraints',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
index ec2b4da1..e58bb463 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
@@ -1,10 +1,12 @@
define('pgadmin.node.index', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'backbone', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.alertifyjs',
- 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.collection',
+ 'pgadmin.backform', 'pgadmin.backgrid',
+ 'pgadmin.node.schema.dir/schema_child_tree_node',
+ 'pgadmin.browser.collection',
], function(
gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Alertify, Backform,
- Backgrid
+ Backgrid, SchemaChildTreeNode
) {
if (!pgBrowser.Nodes['coll-index']) {
@@ -13,7 +15,6 @@ define('pgadmin.node.index', [
node: 'index',
label: gettext('Indexes'),
type: 'coll-index',
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
sqlAlterHelp: 'sql-alterindex.html',
sqlCreateHelp: 'sql-createindex.html',
dialogHelp: url_for('help.static', {'filename': 'index_dialog.html'}),
@@ -215,7 +216,6 @@ define('pgadmin.node.index', [
if (!pgBrowser.Nodes['index']) {
pgAdmin.Browser.Nodes['index'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
parent_type: ['table', 'view', 'mview', 'partition'],
collection_type: ['coll-table', 'coll-view'],
sqlAlterHelp: 'sql-alterindex.html',
@@ -266,8 +266,8 @@ define('pgadmin.node.index', [
},
]);
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ canDrop: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
+ canDropCascade: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
model: pgAdmin.Browser.Node.Model.extend({
idAttribute: 'oid',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js
index d807304e..74649721 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js
@@ -2,10 +2,12 @@ define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
'pgadmin.alertifyjs', 'pgadmin.backform', 'pgadmin.backgrid',
+ 'pgadmin.node.schema.dir/schema_child_tree_node',
'pgadmin.browser.collection', 'pgadmin.browser.table.partition.utils',
],
function(
- gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, Backform, Backgrid
+ gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, Backform, Backgrid,
+ SchemaChildTreeNode
) {
if (!pgBrowser.Nodes['coll-partition']) {
@@ -13,7 +15,6 @@ function(
pgAdmin.Browser.Collection.extend({
node: 'partition',
label: gettext('Partitions'),
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
type: 'coll-partition',
columns: [
'name', 'schema', 'partition_value', 'is_partitioned', 'description',
@@ -80,36 +81,6 @@ function(
},
]);
},
- getTreeNodeHierarchy: function(i) {
- var idx = 0,
- res = {},
- t = pgBrowser.tree;
-
- do {
- var d = t.itemData(i);
- if (
- d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId
- ) {
- if (d._type == 'partition' && 'partition' in res) {
- if (!('table' in res)) {
- res['table'] = _.extend({}, d, {'priority': idx});
- idx -= 1;
- }
- } else if (d._type == 'table') {
- if (!('table' in res)) {
- res['table'] = _.extend({}, d, {'priority': idx});
- idx -= 1;
- }
- } else {
- res[d._type] = _.extend({}, d, {'priority': idx});
- idx -= 1;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- } while (i);
-
- return res;
- },
generate_url: function(item, type, d, with_id, info) {
if (_.indexOf([
'stats', 'statistics', 'dependency', 'dependent', 'reset',
@@ -133,8 +104,8 @@ function(
encodeURIComponent(info['partition']._id)
).value();
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ canDrop: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
+ canDropCascade: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
callbacks: {
/* Enable trigger(s) on table */
enable_triggers_on_table: function(args) {
@@ -1189,34 +1160,7 @@ function(
return data;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create table
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-table' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null;
- var prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
+ canCreate: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
// Check to whether table has disable trigger(s)
canCreate_with_trigger_enable: function(itemData, item, data) {
if(this.canCreate.apply(this, [itemData, item, data])) {
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js
index 3af61754..354909f3 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js
@@ -16,7 +16,6 @@ define('pgadmin.node.rule', [
node: 'rule',
label: gettext('Rules'),
type: 'coll-rule',
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
columns: ['name', 'owner', 'comment'],
});
}
@@ -35,7 +34,6 @@ define('pgadmin.node.rule', [
*/
if (!pgBrowser.Nodes['rule']) {
pgAdmin.Browser.Nodes['rule'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
parent_type: ['table','view', 'partition'],
type: 'rule',
sqlAlterHelp: 'sql-alterrule.html',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/enable_disable_triggers.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/enable_disable_triggers.js
new file mode 100644
index 00000000..2d792043
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/enable_disable_triggers.js
@@ -0,0 +1,52 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import axios from 'axios';
+
+export function disableTriggers(tree, alertify, generateUrl, args) {
+ return setTriggers(tree, alertify, generateUrl, args, {enable: 'false' });
+}
+export function enableTriggers(tree, alertify, generateUrl, args) {
+ return setTriggers(tree, alertify, generateUrl, args, {enable: 'true' });
+}
+
+function setTriggers(tree, alertify, generateUrl, args, params) {
+ const treeNode = retrieveTreeNode(args, tree);
+
+ if (!treeNode || treeNode.getData() === null || treeNode.getData() === undefined)
+ return false;
+
+ axios.put(
+ generateUrl(treeNode.getHtmlIdentifier(), 'set_trigger', treeNode.getData(), true),
+ params
+ )
+ .then((res) => {
+ if (res.data.success === 1) {
+ alertify.success(res.data.info);
+ treeNode.reload(tree);
+ }
+ })
+ .catch((xhr) => {
+ try {
+ const err = xhr.response.data;
+ if (err.success === 0) {
+ alertify.error(err.errormsg);
+ }
+ } catch (e) {
+ console.warn(e.stack || e);
+ }
+ treeNode.unload(tree);
+ });
+}
+
+function retrieveTreeNode(args, tree) {
+ const input = args || {};
+ const domElementIdentifier = input.item || tree.selected();
+ return tree.findNodeByDomElement(domElementIdentifier);
+}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js
index d440bf04..1f9bcf97 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js
@@ -1,13 +1,16 @@
define('pgadmin.node.table', [
+ 'pgadmin.tables.js/enable_disable_triggers',
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
'pgadmin.alertifyjs', 'pgadmin.backform', 'pgadmin.backgrid',
'pgadmin.tables.js/show_advanced_tab',
- 'pgadmin.browser.collection', 'pgadmin.node.column',
- 'pgadmin.node.constraints', 'pgadmin.browser.table.partition.utils',
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+ 'pgadmin.node.column', 'pgadmin.node.constraints',
+ 'pgadmin.browser.table.partition.utils',
], function(
+ tableFunctions,
gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, Backform, Backgrid,
- ShowAdvancedTab
+ ShowAdvancedTab, SchemaChild
) {
if (!pgBrowser.Nodes['coll-table']) {
@@ -25,8 +28,7 @@ define('pgadmin.node.table', [
}
if (!pgBrowser.Nodes['table']) {
- pgBrowser.Nodes['table'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+ pgBrowser.Nodes['table'] = SchemaChild.SchemaChildNode.extend({
type: 'table',
label: gettext('Table'),
collection_type: 'coll-table',
@@ -39,7 +41,6 @@ define('pgadmin.node.table', [
sqlAlterHelp: 'sql-altertable.html',
sqlCreateHelp: 'sql-createtable.html',
dialogHelp: url_for('help.static', {'filename': 'table_dialog.html'}),
- parent_type: ['schema', 'catalog'],
hasScriptTypes: ['create', 'select', 'insert', 'update', 'delete'],
height: '95%',
width: '85%',
@@ -113,51 +114,24 @@ define('pgadmin.node.table', [
this.handle_cache, this
);
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
callbacks: {
/* Enable trigger(s) on table */
enable_triggers_on_table: function(args) {
- var params = {'enable': true };
- this.callbacks.set_triggers.apply(this, [args, params]);
+ tableFunctions.enableTriggers(
+ pgBrowser.treeMenu,
+ Alertify,
+ this.generate_url.bind(this),
+ args
+ );
},
/* Disable trigger(s) on table */
disable_triggers_on_table: function(args) {
- var params = {'enable': false };
- this.callbacks.set_triggers.apply(this, [args, params]);
- },
- set_triggers: function(args, params) {
- // This function will send request to enable or
- // disable triggers on table level
- var input = args || {},
- obj = this,
- t = pgBrowser.tree,
- i = input.item || t.selected(),
- d = i && i.length == 1 ? t.itemData(i) : undefined;
- if (!d)
- return false;
-
- $.ajax({
- url: obj.generate_url(i, 'set_trigger' , d, true),
- type:'PUT',
- data: params,
- dataType: 'json',
- success: function(res) {
- if (res.success == 1) {
- Alertify.success(res.info);
- t.unload(i);
- t.setInode(i);
- t.deselect(i);
- setTimeout(function() {
- t.select(i);
- }, 10);
- }
- },
- error: function(xhr, status, error) {
- Alertify.pgRespErrorNotify(xhr, error);
- t.unload(i);
- },
- });
+ tableFunctions.disableTriggers(
+ pgBrowser.treeMenu,
+ Alertify,
+ this.generate_url.bind(this),
+ args
+ );
},
/* Truncate table */
truncate_table: function(args) {
@@ -1299,55 +1273,15 @@ define('pgadmin.node.table', [
return data;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create table
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-table' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
// Check to whether table has disable trigger(s)
canCreate_with_trigger_enable: function(itemData, item, data) {
- if(this.canCreate.apply(this, [itemData, item, data])) {
- // We are here means we can create menu, now let's check condition
- if(itemData.tigger_count > 0) {
- return true;
- } else {
- return false;
- }
- }
+ return itemData.tigger_count > 0 &&
+ this.canCreate.apply(this, [itemData, item, data]);
},
// Check to whether table has enable trigger(s)
canCreate_with_trigger_disable: function(itemData, item, data) {
- if(this.canCreate.apply(this, [itemData, item, data])) {
- // We are here means we can create menu, now let's check condition
- if(itemData.tigger_count > 0 && itemData.has_enable_triggers > 0) {
- return true;
- } else {
- return false;
- }
- }
+ return itemData.tigger_count > 0 && itemData.has_enable_triggers > 0 &&
+ this.canCreate.apply(this, [itemData, item, data]);
},
onTableUpdated: function(_node, _oldNodeData, _newNodeData) {
var key, childIDs;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
index a2c27188..a6e79ce2 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
@@ -1,8 +1,13 @@
define('pgadmin.node.trigger', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
- 'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'backform', 'pgadmin.alertifyjs',
+ 'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
+ 'pgadmin.backform', 'pgadmin.alertifyjs',
+ 'pgadmin.node.schema.dir/schema_child_tree_node',
'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, alertify) {
+], function(
+ gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, alertify,
+ SchemaChildTreeNode
+) {
Backform.CustomSwitchControl = Backform.SwitchControl.extend({
template: _.template([
@@ -29,14 +34,12 @@ define('pgadmin.node.trigger', [
node: 'trigger',
label: gettext('Triggers'),
type: 'coll-trigger',
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
columns: ['name', 'description'],
});
}
if (!pgBrowser.Nodes['trigger']) {
pgAdmin.Browser.Nodes['trigger'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
parent_type: ['table', 'view', 'partition'],
collection_type: ['coll-table', 'coll-view'],
type: 'trigger',
@@ -175,8 +178,8 @@ define('pgadmin.node.trigger', [
});
},
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ canDrop: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
+ canDropCascade: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
model: pgAdmin.Browser.Node.Model.extend({
defaults: {
name: undefined,
@@ -618,50 +621,16 @@ define('pgadmin.node.trigger', [
return flag;
},
}),
- // Below function will enable right click menu for creating column
- canCreate: function(itemData, item, data) {
- // If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData, parents = [];
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to c reate table
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
- parents.push(d._type);
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // If node is under catalog then do not allow 'create' menu
- if (_.indexOf(parents, 'catalog') > -1) {
- return false;
- } else {
- return true;
- }
- },
+ canCreate: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
// Check to whether trigger is disable ?
canCreate_with_trigger_enable: function(itemData, item, data) {
- if(this.canCreate.apply(this, [itemData, item, data])) {
- // We are here means we can create menu, now let's check condition
- if(itemData.icon === 'icon-trigger-bad') {
- return true;
- } else {
- return false;
- }
- }
+ return itemData.icon === 'icon-trigger-bad' &&
+ this.canCreate.apply(this, [itemData, item, data]);
},
// Check to whether trigger is enable ?
canCreate_with_trigger_disable: function(itemData, item, data) {
- if(this.canCreate.apply(this, [itemData, item, data])) {
- // We are here means we can create menu, now let's check condition
- if(itemData.icon === 'icon-trigger') {
- return true;
- } else {
- return false;
- }
- }
+ return itemData.icon === 'icon-trigger' &&
+ this.canCreate.apply(this, [itemData, item, data]);
},
});
}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js
index c1c24861..5860a752 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js
@@ -1,8 +1,12 @@
define('pgadmin.node.type', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
- 'pgadmin.backgrid', 'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid) {
+ 'pgadmin.backgrid', 'pgadmin.node.schema.dir/child',
+ 'pgadmin.browser.collection',
+], function(
+ gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid,
+ schemaChild
+) {
if (!pgBrowser.Nodes['coll-type']) {
pgBrowser.Nodes['coll-type'] =
@@ -245,7 +249,7 @@ define('pgadmin.node.type', [
});
if (!pgBrowser.Nodes['type']) {
- pgBrowser.Nodes['type'] = pgBrowser.Node.extend({
+ pgBrowser.Nodes['type'] = schemaChild.SchemaChildNode.extend({
type: 'type',
sqlAlterHelp: 'sql-altertype.html',
sqlCreateHelp: 'sql-createtype.html',
@@ -254,7 +258,6 @@ define('pgadmin.node.type', [
collection_type: 'coll-type',
hasSQL: true,
hasDepends: true,
- parent_type: ['schema', 'catalog'],
Init: function() {
/* Avoid multiple registration of menus */
if (this.initialized)
@@ -284,8 +287,6 @@ define('pgadmin.node.type', [
]);
},
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
ext_funcs: undefined,
model: pgBrowser.Node.Model.extend({
defaults: {
@@ -911,34 +912,6 @@ define('pgadmin.node.type', [
return result;
},
}),
- canCreate: function(itemData, item, data) {
- //If check is false then , we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to create table
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-type' == d._type) {
- //Check if we are not child of catalog
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- },
});
}
return pgBrowser.Nodes['type'];
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 073ef5cb..dcfdd54b 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
@@ -1,8 +1,12 @@
define('pgadmin.node.mview', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'sources/pgadmin', 'pgadmin.alertifyjs', 'pgadmin.browser',
- 'pgadmin.backform', 'pgadmin.browser.server.privilege',
-], function(gettext, url_for, $, _, pgAdmin, Alertify, pgBrowser, Backform) {
+ 'pgadmin.backform', 'pgadmin.node.schema.dir/child',
+ 'pgadmin.browser.server.privilege',
+], function(
+ gettext, url_for, $, _, pgAdmin, Alertify, pgBrowser, Backform,
+ schemaChild
+) {
/**
Create and add a view collection into nodes
@@ -33,19 +37,16 @@ define('pgadmin.node.mview', [
view option in the context menu
*/
if (!pgBrowser.Nodes['mview']) {
- pgBrowser.Nodes['mview'] = pgBrowser.Node.extend({
- parent_type: ['schema', 'catalog'],
+ pgBrowser.Nodes['mview'] = schemaChild.SchemaChildNode.extend({
type: 'mview',
sqlAlterHelp: 'sql-altermaterializedview.html',
sqlCreateHelp: 'sql-creatematerializedview.html',
dialogHelp: url_for('help.static', {'filename': 'materialized_view_dialog.html'}),
label: gettext('Materialized View'),
- hasSQL: true,
+ hasSQL: true,
hasDepends: true,
hasScriptTypes: ['create', 'select'],
collection_type: 'coll-mview',
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
Init: function() {
// Avoid mulitple registration of menus
@@ -236,43 +237,6 @@ define('pgadmin.node.mview', [
}),
- /**
- Show or hide create view menu option on parent node
- and hide for system view in catalogs.
- */
- canCreate: function(itemData, item, data) {
-
- // If check is false then, we will allow create menu
- if (data && data.check === false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
-
- // To iterate over tree to check parent node
- while (i) {
-
- // If it is schema then allow user to create view
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-mview' == d._type) {
-
- // Check if we are not child of view
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
-
- // by default we do not want to allow create menu
- return true;
- },
refresh_mview: function(args) {
var input = args || {},
obj = this,
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 5755a509..cd61ef21 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
@@ -1,9 +1,12 @@
define('pgadmin.node.view', [
- 'sources/gettext',
- 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin',
- 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.browser.server.privilege',
+ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+ 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
+ 'pgadmin.node.schema.dir/child', 'pgadmin.browser.server.privilege',
'pgadmin.node.rule',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform) {
+], function(
+ gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, schemaChild
+) {
+
/**
Create and add a view collection into nodes
@@ -28,14 +31,9 @@ define('pgadmin.node.view', [
under which this node to display
@param {variable} type - Type of Node
@param {variable} hasSQL - To show SQL tab
- @param {variable} canDrop - Adds drop view option
- in the context menu
- @param {variable} canDropCascade - Adds drop Cascade
- view option in the context menu
*/
if (!pgBrowser.Nodes['view']) {
- pgBrowser.Nodes['view'] = pgBrowser.Node.extend({
- parent_type: ['schema', 'catalog'],
+ pgBrowser.Nodes['view'] = schemaChild.SchemaChildNode.extend({
type: 'view',
sqlAlterHelp: 'sql-alterview.html',
sqlCreateHelp: 'sql-createview.html',
@@ -45,8 +43,6 @@ define('pgadmin.node.view', [
hasDepends: true,
hasScriptTypes: ['create', 'select', 'insert'],
collection_type: 'coll-view',
- canDrop: pgBrowser.Nodes['schema'].canChildDrop,
- canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
Init: function() {
// Avoid mulitple registration of menus
@@ -197,45 +193,6 @@ define('pgadmin.node.view', [
return false;
},
}),
-
- /**
- Show or hide create view menu option on parent node
- and hide for system view in catalogs.
- */
- canCreate: function(itemData, item, data) {
-
- // If check is false then, we will allow create menu
- if (data && data.check == false)
- return true;
-
- var t = pgBrowser.tree, i = item, d = itemData;
-
- // To iterate over tree to check parent node
- while (i) {
-
- // If it is schema then allow user to create view
- if (_.indexOf(['schema'], d._type) > -1)
- return true;
-
- if ('coll-view' == d._type) {
-
- // Check if we are not child of view
- var prev_i = t.hasParent(i) ? t.parent(i) : null,
- prev_d = prev_i ? t.itemData(prev_i) : null;
- if( prev_d._type == 'catalog') {
- return false;
- } else {
- return true;
- }
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
-
- // by default we do not want to allow create menu
- return true;
-
- },
});
}
diff --git a/web/pgadmin/browser/static/js/collection.js b/web/pgadmin/browser/static/js/collection.js
index 05f0edd5..67e44214 100644
--- a/web/pgadmin/browser/static/js/collection.js
+++ b/web/pgadmin/browser/static/js/collection.js
@@ -115,17 +115,17 @@ define([
// Fetch Data
collection.fetch({
reset: true,
- error: function(xhr, error, message) {
+ error: function(model, error, xhr) {
pgBrowser.Events.trigger(
'pgadmin:collection:retrieval:error', 'properties', xhr, error,
- message, item, that
+ error.message, item, that
);
if (!Alertify.pgHandleItemError(
- xhr, error, message, {item: item, info: info}
+ xhr, error, error.message, {item: item, info: info}
)) {
Alertify.pgNotifier(error, xhr, S(
gettext('Error retrieving properties - %s.')
- ).sprintf(message || that.label).value(), function() {
+ ).sprintf(error.message || that.label).value(), function() {
console.warn(arguments);
});
}
diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js
index ad582483..94baeabf 100644
--- a/web/pgadmin/browser/static/js/node.js
+++ b/web/pgadmin/browser/static/js/node.js
@@ -1,9 +1,13 @@
define('pgadmin.browser.node', [
+ 'sources/tree/pgadmin_tree_node',
'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin',
'pgadmin.browser.menu', 'backbone', 'pgadmin.alertifyjs', 'pgadmin.browser.datamodel',
'backform', 'sources/browser/generate_url', 'sources/utils', 'pgadmin.browser.utils',
'pgadmin.backform',
-], function(gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl, commonUtils) {
+], function(
+ pgadminTreeNode,
+ gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl, commonUtils
+) {
var wcDocker = window.wcDocker,
keyCode = {
@@ -28,7 +32,7 @@ define('pgadmin.browser.node', [
//
// It is unlikely - we will instantiate an object for this class.
// (Inspired by Backbone.extend function)
- pgBrowser.Node.extend = function(props) {
+ pgBrowser.Node.extend = function(props, initialize) {
var parent = this;
var child;
@@ -44,6 +48,10 @@ define('pgadmin.browser.node', [
// Make sure - a child have all the callbacks of the parent.
child.callbacks = _.extend({}, parent.callbacks, props.callbacks);
+ // Let's not bind the callbacks, or initialize the child.
+ if (initialize === false)
+ return child;
+
var bindToChild = function(cb) {
if (typeof(child.callbacks[cb]) == 'function') {
child.callbacks[cb] = child.callbacks[cb].bind(child);
@@ -1566,7 +1574,6 @@ define('pgadmin.browser.node', [
* depends, statistics
*/
generate_url: function(item, type, d, with_id, info) {
-
var opURL = {
'create': 'obj',
'drop': 'obj',
@@ -1608,24 +1615,7 @@ define('pgadmin.browser.node', [
Collection: pgBrowser.DataCollection,
// Base class for Node Data Model
Model: pgBrowser.DataModel,
- getTreeNodeHierarchy: function(i) {
- var idx = 0,
- res = {},
- t = pgBrowser.tree,
- d;
- do {
- d = t.itemData(i);
- if (d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId) {
- res[d._type] = _.extend({}, d, {
- 'priority': idx,
- });
- idx -= 1;
- }
- i = t.hasParent(i) ? t.parent(i) : null;
- } while (i);
-
- return res;
- },
+ getTreeNodeHierarchy: pgadminTreeNode.getTreeNodeHierarchyFromIdentifier.bind(pgBrowser),
cache: function(url, node_info, level, data) {
var cached = this.cached = this.cached || {},
hash = url,
diff --git a/web/pgadmin/static/js/alertify/dialog.js b/web/pgadmin/static/js/alertify/dialog.js
new file mode 100644
index 00000000..5a8646f9
--- /dev/null
+++ b/web/pgadmin/static/js/alertify/dialog.js
@@ -0,0 +1,129 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from '../gettext';
+import {sprintf} from 'sprintf-js';
+import {DialogFactory} from './dialog_factory';
+import Backform from '../backform.pgadmin';
+import {getTreeNodeHierarchyFromIdentifier} from '../tree/pgadmin_tree_node';
+
+/**
+ * This class can be extended to create new dialog boxes.
+ * Examples of this can be found in:
+ * `web/pgadmin/static/js/backup/backup_dialog.js`
+ *
+ * Do not forget to add the new Dialog type to the `DialogFactory`
+ */
+export class Dialog {
+ constructor(errorAlertTitle,
+ dialogContainerSelector,
+ pgBrowser, $, alertify, DialogModel,
+ backform = Backform) {
+ this.errorAlertTitle = errorAlertTitle;
+ this.alertify = alertify;
+ this.pgBrowser = pgBrowser;
+ this.jquery = $;
+ this.dialogModel = DialogModel;
+ this.backform = backform;
+ this.dialogContainerSelector = dialogContainerSelector;
+ }
+
+ retrieveAncestorOfTypeServer(item) {
+ let serverInformation = null;
+ let aciTreeItem = item || this.pgBrowser.treeMenu.selected();
+ let treeNode = this.pgBrowser.treeMenu.findNodeByDomElement(aciTreeItem);
+
+ while (treeNode) {
+ const node_data = treeNode.getData();
+ if (node_data._type === 'server') {
+ serverInformation = node_data;
+ break;
+ }
+
+ if (treeNode.hasParent()) {
+ treeNode = treeNode.parent();
+ } else {
+ this.alertify.alert(
+ gettext(this.errorAlertTitle),
+ gettext('Please select server or child node from the browser tree.')
+ );
+ break;
+ }
+ }
+ return serverInformation;
+ }
+
+ hasBinariesConfiguration(serverInformation) {
+ const module = 'paths';
+ let preference_name = 'pg_bin_dir';
+ let msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
+
+ if ((serverInformation.type && serverInformation.type === 'ppas') ||
+ serverInformation.server_type === 'ppas') {
+ preference_name = 'ppas_bin_dir';
+ msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
+ }
+ const preference = this.pgBrowser.get_preference(module, preference_name);
+
+ if (preference) {
+ if (!preference.value) {
+ this.alertify.alert(gettext('Configuration required'), msg);
+ return false;
+ }
+ } else {
+ this.alertify.alert(
+ gettext(this.errorAlertTitle),
+ sprintf(gettext('Failed to load preference %s of module %s'), preference_name, module)
+ );
+ return false;
+ }
+ return true;
+ }
+
+ dialogName() {
+ return undefined;
+ }
+
+ createOrGetDialog(dialogTitle, typeOfDialog) {
+ const dialogName = this.dialogName(typeOfDialog);
+
+ if (!this.alertify[dialogName]) {
+ const self = this;
+ this.alertify.dialog(dialogName, function factory() {
+ return self.dialogFactory(dialogTitle, typeOfDialog);
+ });
+ }
+ return this.alertify[dialogName];
+ }
+
+ dialogFactory(dialogTitle, typeOfDialog) {
+ const factory = new DialogFactory(
+ this.pgBrowser,
+ this.jquery,
+ this.alertify,
+ this.dialogModel,
+ this.backform,
+ this.dialogContainerSelector);
+ return factory.create(dialogTitle, typeOfDialog);
+ }
+
+ canExecuteOnCurrentDatabase(aciTreeItem) {
+ const treeInfo = getTreeNodeHierarchyFromIdentifier.apply(this.pgBrowser, [aciTreeItem]);
+
+ if (treeInfo.database && treeInfo.database._label.indexOf('=') >= 0) {
+ this.alertify.alert(
+ gettext(this.errorAlertTitle),
+ gettext('Databases with = symbols in the name cannot be backed up or restored using this utility.')
+ );
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/web/pgadmin/static/js/alertify/dialog_factory.js b/web/pgadmin/static/js/alertify/dialog_factory.js
new file mode 100644
index 00000000..500140b8
--- /dev/null
+++ b/web/pgadmin/static/js/alertify/dialog_factory.js
@@ -0,0 +1,52 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import * as BackupDialog from '../../../tools/backup/static/js/backup_dialog_wrapper';
+import {RestoreDialogWrapper} from '../../../tools/restore/static/js/restore_dialog_wrapper';
+
+export class DialogFactory {
+ constructor(pgBrowser, $,
+ alertify, DialogModel,
+ backform, dialogContainerSelector) {
+ this.pgBrowser = pgBrowser;
+ this.jquery = $;
+ this.alertify = alertify;
+ this.dialogModel = DialogModel;
+ this.backform = backform;
+ this.dialogContainerSelector = dialogContainerSelector;
+ }
+
+ create(dialogTitle, typeOfDialog) {
+ if (typeOfDialog === 'restore') {
+ return this.createRestoreDialog(dialogTitle, typeOfDialog);
+ } else {
+ return this.createBackupDialog(dialogTitle, typeOfDialog);
+ }
+ }
+
+ createRestoreDialog(dialogTitle, typeOfDialog) {
+ return new RestoreDialogWrapper(
+ this.dialogContainerSelector, dialogTitle, typeOfDialog,
+ this.jquery,
+ this.pgBrowser,
+ this.alertify,
+ this.dialogModel,
+ this.backform);
+ }
+
+ createBackupDialog(dialogTitle, typeOfDialog) {
+ return new BackupDialog.BackupDialogWrapper(
+ this.dialogContainerSelector, dialogTitle, typeOfDialog,
+ this.jquery,
+ this.pgBrowser,
+ this.alertify,
+ this.dialogModel,
+ this.backform);
+ }
+}
diff --git a/web/pgadmin/static/js/alertify/dialog_wrapper.js b/web/pgadmin/static/js/alertify/dialog_wrapper.js
new file mode 100644
index 00000000..b5ff8204
--- /dev/null
+++ b/web/pgadmin/static/js/alertify/dialog_wrapper.js
@@ -0,0 +1,57 @@
+import * as commonUtils from '../utils';
+
+export class DialogWrapper {
+ constructor(
+ dialogContainerSelector, dialogTitle, jquery, pgBrowser,
+ alertify, dialogModel, backform) {
+ this.hooks = {
+ onclose: function () {
+ if (this.view) {
+ this.view.remove({
+ data: true,
+ internal: true,
+ silent: true,
+ });
+ }
+ },
+ };
+ this.dialogContainerSelector = dialogContainerSelector;
+ this.dialogTitle = dialogTitle;
+ this.jquery = jquery;
+ this.pgBrowser = pgBrowser;
+ this.alertify = alertify;
+ this.dialogModel = dialogModel;
+ this.backform = backform;
+ }
+
+ build() {
+ this.alertify.pgDialogBuild.apply(this);
+ }
+
+ wasHelpButtonPressed(e) {
+ return e.button.element.name === 'dialog_help'
+ || e.button.element.name === 'object_help';
+ }
+
+ getSelectedNodeData(selectedTreeNode) {
+ if (!this.isNodeSelected(selectedTreeNode)) {
+ return undefined;
+ }
+ const treeNodeData = selectedTreeNode.getData();
+ if (treeNodeData) {
+ return treeNodeData;
+ }
+ return undefined;
+ }
+
+ focusOnDialog(dialog) {
+ dialog.$el.attr('tabindex', -1);
+ this.pgBrowser.keyboardNavigation.getDialogTabNavigator(dialog);
+ const container = dialog.$el.find('.tab-content:first > .tab-pane.active:first');
+ commonUtils.findAndSetFocus(container);
+ }
+
+ isNodeSelected(selectedTreeNode) {
+ return selectedTreeNode;
+ }
+}
diff --git a/web/pgadmin/static/js/nodes/supported_database_node.js b/web/pgadmin/static/js/nodes/supported_database_node.js
new file mode 100644
index 00000000..fde1cf98
--- /dev/null
+++ b/web/pgadmin/static/js/nodes/supported_database_node.js
@@ -0,0 +1,37 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {isValidTreeNodeData} from 'sources/tree/tree';
+
+function checkAllowConnIfDatabaseNode(treeNodeData) {
+ return (treeNodeData._type === 'database' && treeNodeData.allowConn)
+ || treeNodeData._type !== 'database';
+}
+
+function ancestorWithTypeCatalog(treeNode) {
+ return treeNode.anyFamilyMember((node) => {
+ return node.getData()._type === 'catalog';
+ });
+}
+
+export function enabled(tree, supportedNodes, treeNodeData, domTreeNode) {
+ if (!isValidTreeNodeData(treeNodeData))
+ return false;
+
+ let treeNode = tree.findNodeByDomElement(domTreeNode);
+ if (!treeNode) {
+ return false;
+ }
+
+ return checkAllowConnIfDatabaseNode(treeNodeData) &&
+ _.indexOf(supportedNodes, treeNodeData._type) !== -1 &&
+ !ancestorWithTypeCatalog(treeNode);
+}
+
+
diff --git a/web/pgadmin/static/js/tree/pgadmin_tree_node.js b/web/pgadmin/static/js/tree/pgadmin_tree_node.js
new file mode 100644
index 00000000..00f10d24
--- /dev/null
+++ b/web/pgadmin/static/js/tree/pgadmin_tree_node.js
@@ -0,0 +1,72 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+/**
+ * This method received pgBrowser and new TreeNode object
+ *
+ * This method retrieves all the data that exists in the tree node and in
+ * `pgBrowser.Nodes` for all the parent node of the provided node.
+ *
+ * The 2 condition to get the information from pgBrowser.Nodes are:
+ * 1 - the variable _type of the tree node
+ * 2 - the presence of hasId in the pgBrowser.Nodes for the specific node
+ *
+ * Number 2 is used to ignore coll-* nodes as they do not add any useful
+ * information
+ */
+export function getTreeNodeHierarchyFromElement(pgBrowser, treeNode) {
+ return getTreeNodeHierarchy.call(pgBrowser, treeNode);
+}
+
+/**
+ * This method received an ACI Tree JQuery node
+ *
+ * NOTE: this function need to be called on pgBrowser instance.
+ * getTreeNodeHierarchyFromIdentifier.apply(pgBrowser, [aciTreeNodeIdentifier])
+ *
+ * This method retrieves all the data that exists in the tree node and in
+ * `pgBrowser.Nodes` for all the parent node of the provided node.
+ *
+ * The 2 condition to get the information from pgBrowser.Nodes are:
+ * 1 - the variable _type of the tree node
+ * 2 - the presence of hasId in the pgBrowser.Nodes for the specific node
+ *
+ * Number 2 is used to ignore coll-* nodes as they do not add any useful
+ * information
+ */
+export function getTreeNodeHierarchyFromIdentifier(aciTreeNodeIdentifier) {
+ let identifier = this.treeMenu.translateTreeNodeIdFromACITree(aciTreeNodeIdentifier);
+ let currentNode = this.treeMenu.findNode(identifier);
+ return getTreeNodeHierarchy.call(this, currentNode);
+}
+
+export function getTreeNodeHierarchy(currentNode) {
+ let idx = 0;
+ let result = {};
+
+ do {
+ const currentNodeData = currentNode.getData();
+ if (currentNodeData._type in this.Nodes && this.Nodes[currentNodeData._type].hasId) {
+ const nodeType = mapType(currentNodeData._type);
+ if (result[nodeType] === undefined) {
+ result[nodeType] = _.extend({}, currentNodeData, {
+ 'priority': idx,
+ });
+ idx -= 1;
+ }
+ }
+ currentNode = currentNode.hasParent() ? currentNode.parent() : null;
+ } while (currentNode);
+
+ return result;
+}
+
+function mapType(type) {
+ return type === 'partition' ? 'table' : type;
+}
diff --git a/web/pgadmin/static/js/tree/tree.js b/web/pgadmin/static/js/tree/tree.js
index 01edb6c3..65330469 100644
--- a/web/pgadmin/static/js/tree/tree.js
+++ b/web/pgadmin/static/js/tree/tree.js
@@ -59,17 +59,20 @@ export class TreeNode {
tree.aciTreeApi.unload(this.domNode);
}
- anyParent(condition) {
+ /*
+ * Find the ancestor with matches this condition
+ */
+ ancestorNode(condition) {
let node = this;
while (node.hasParent()) {
node = node.parent();
if (condition(node)) {
- return true;
+ return node;
}
}
- return false;
+ return null;
}
/**
@@ -81,7 +84,10 @@ export class TreeNode {
return true;
}
- return this.anyParent(condition);
+ return !!this.ancestorNode(condition);
+ }
+ anyParent(condition) {
+ return !!this.ancestorNode(condition);
}
}
@@ -210,3 +216,7 @@ function findInTree(rootNode, path) {
}
})(rootNode);
}
+
+export function isValidTreeNodeData(treeNodeData) {
+ return !_.isUndefined(treeNodeData) && !_.isNull(treeNodeData);
+}
diff --git a/web/pgadmin/tools/backup/static/js/backup.js b/web/pgadmin/tools/backup/static/js/backup.js
index 1b0b3628..d6fd48c5 100644
--- a/web/pgadmin/tools/backup/static/js/backup.js
+++ b/web/pgadmin/tools/backup/static/js/backup.js
@@ -3,9 +3,12 @@ define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'pgadmin.alertifyjs', 'backbone', 'pgadmin.backgrid',
'pgadmin.backform', 'pgadmin.browser', 'sources/utils',
+ 'tools/backup/static/js/menu_utils',
+ 'tools/backup/static/js/backup_dialog',
+ 'sources/nodes/supported_database_node',
], function(
gettext, url_for, $, _, S, alertify, Backbone, Backgrid, Backform, pgBrowser,
-commonUtils
+ commonUtils, menuUtils, globalBackupDialog, supportedNodes
) {
// if module is already initialized, refer to that.
@@ -394,48 +397,6 @@ commonUtils
this.initialized = true;
- // Define list of nodes on which backup context menu option appears
- var backup_supported_nodes = [
- 'database', 'schema', 'table', 'partition',
- ];
-
- /**
- Enable/disable backup menu in tools based
- on node selected
- if selected node is present in supported_nodes,
- menu will be enabled otherwise disabled.
- Also, hide it for system view in catalogs
- */
- var menu_enabled = function(itemData, item) {
- var t = pgBrowser.tree,
- i = item,
- d = itemData,
- parent_item = t.hasParent(i) ? t.parent(i) : null,
- parent_data = parent_item ? t.itemData(parent_item) : null;
-
- if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
- if (_.indexOf(backup_supported_nodes, d._type) !== -1 &&
- parent_data._type != 'catalog') {
- if (d._type == 'database' && d.allowConn)
- return true;
- else if (d._type != 'database')
- return true;
- else
- return false;
- } else
- return false;
- } else
- return false;
- };
-
- var menu_enabled_server = function(itemData) {
- // If server node selected && connected
- if (!_.isUndefined(itemData) && !_.isNull(itemData))
- return (('server' === itemData._type) && itemData.connected);
- else
- return false;
- };
-
// Define the nodes on which the menus to be appear
var menus = [{
name: 'backup_global',
@@ -445,7 +406,7 @@ commonUtils
priority: 12,
label: gettext('Backup Globals...'),
icon: 'fa fa-floppy-o',
- enable: menu_enabled_server,
+ enable: menuUtils.menuEnabledServer,
}, {
name: 'backup_server',
module: this,
@@ -454,7 +415,7 @@ commonUtils
priority: 12,
label: gettext('Backup Server...'),
icon: 'fa fa-floppy-o',
- enable: menu_enabled_server,
+ enable: menuUtils.menuEnabledServer,
}, {
name: 'backup_global_ctx',
module: this,
@@ -464,7 +425,7 @@ commonUtils
priority: 12,
label: gettext('Backup Globals...'),
icon: 'fa fa-floppy-o',
- enable: menu_enabled_server,
+ enable: menuUtils.menuEnabledServer,
}, {
name: 'backup_server_ctx',
module: this,
@@ -474,7 +435,7 @@ commonUtils
priority: 12,
label: gettext('Backup Server...'),
icon: 'fa fa-floppy-o',
- enable: menu_enabled_server,
+ enable: menuUtils.menuEnabledServer,
}, {
name: 'backup_object',
module: this,
@@ -483,20 +444,24 @@ commonUtils
priority: 11,
label: gettext('Backup...'),
icon: 'fa fa-floppy-o',
- enable: menu_enabled,
+ enable: supportedNodes.enabled.bind(
+ null, pgBrowser.treeMenu, menuUtils.backupSupportedNodes
+ ),
}];
- for (var idx = 0; idx < backup_supported_nodes.length; idx++) {
+ for (var idx = 0; idx < menuUtils.backupSupportedNodes.length; idx++) {
menus.push({
- name: 'backup_' + backup_supported_nodes[idx],
- node: backup_supported_nodes[idx],
+ name: 'backup_' + menuUtils.backupSupportedNodes[idx],
+ node: menuUtils.backupSupportedNodes[idx],
module: this,
applies: ['context'],
callback: 'backup_objects',
priority: 11,
label: gettext('Backup...'),
icon: 'fa fa-floppy-o',
- enable: menu_enabled,
+ enable: supportedNodes.enabled.bind(
+ null, pgBrowser.treeMenu, menuUtils.backupSupportedNodes
+ ),
});
}
@@ -521,542 +486,25 @@ commonUtils
},
// Callback to draw Backup Dialog for globals/server
- start_backup_global_server: function(action, item, params) {
- var i = item || pgBrowser.tree.selected(),
- server_data = null;
-
- while (i) {
- var node_data = pgBrowser.tree.itemData(i);
- if (node_data._type == 'server') {
- server_data = node_data;
- break;
- }
-
- if (pgBrowser.tree.hasParent(i)) {
- i = $(pgBrowser.tree.parent(i));
- } else {
- alertify.alert(
- gettext('Backup Error'),
- gettext('Please select server or child node from the browser tree.')
- );
- break;
- }
- }
-
- if (!server_data) {
- return;
- }
-
- var module = 'paths',
- preference_name = 'pg_bin_dir',
- msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
-
- if ((server_data.type && server_data.type == 'ppas') ||
- server_data.server_type == 'ppas') {
- preference_name = 'ppas_bin_dir';
- msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
- }
-
- var preference = pgBrowser.get_preference(module, preference_name);
-
- if (preference) {
- if (!preference.value) {
- alertify.alert(gettext('Configuration required'), msg);
- return;
- }
- } else {
- alertify.alert(
- gettext('Backup Error'),
- S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value()
- );
- return;
- }
-
- var of_type = undefined;
-
- // Set Notes according to type of backup
- if (!_.isUndefined(params['globals']) && params['globals']) {
- of_type = 'globals';
- } else {
- of_type = 'server';
- }
-
- var DialogName = 'BackupDialog_' + of_type,
- DialogTitle = ((of_type == 'globals') ?
- gettext('Backup Globals...') :
- gettext('Backup Server...'));
-
- if (!alertify[DialogName]) {
- alertify.dialog(DialogName, function factory() {
- return {
- main: function(title) {
- this.set('title', title);
- },
- build: function() {
- alertify.pgDialogBuild.apply(this);
- },
- setup: function() {
- return {
- buttons: [{
- text: '',
- className: 'btn btn-default pull-left fa fa-lg fa-info',
- attrs: {
- name: 'object_help',
- type: 'button',
- url: 'backup.html',
- label: gettext('Backup'),
- },
- }, {
- text: '',
- key: 112,
- className: 'btn btn-default pull-left fa fa-lg fa-question',
- attrs: {
- name: 'dialog_help',
- type: 'button',
- label: gettext('Backup'),
- url: url_for('help.static', {
- 'filename': 'backup_dialog.html',
- }),
- },
- }, {
- text: gettext('Backup'),
- key: 13,
- className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
- 'data-btn-name': 'backup',
- }, {
- text: gettext('Cancel'),
- key: 27,
- className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
- 'data-btn-name': 'cancel',
- }],
- // Set options for dialog
- options: {
- title: DialogTitle,
- //disable both padding and overflow control.
- padding: !1,
- overflow: !1,
- model: 0,
- resizable: true,
- maximizable: true,
- pinnable: false,
- closableByDimmer: false,
- modal: false,
- },
- };
- },
- hooks: {
- // Triggered when the dialog is closed
- onclose: function() {
- if (this.view) {
- // clear our backform model/view
- this.view.remove({
- data: true,
- internal: true,
- silent: true,
- });
- }
- },
- },
- prepare: function() {
- var self = this;
- // Disable Backup button until user provides Filename
- this.__internal.buttons[2].element.disabled = true;
-
- var $container = $('<div class=\'backup_dialog\'></div>');
- // Find current/selected node
- var t = pgBrowser.tree,
- i = t.selected(),
- d = i && i.length == 1 ? t.itemData(i) : undefined,
- node = d && pgBrowser.Nodes[d._type];
-
- if (!d)
- return;
- // Create treeInfo
- var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
- // Instance of backbone model
- var newModel = new BackupModel({
- type: of_type,
- }, {
- node_info: treeInfo,
- }),
- fields = Backform.generateViewSchema(
- treeInfo, newModel, 'create', node, treeInfo.server, true
- );
-
- var view = this.view = new Backform.Dialog({
- el: $container,
- model: newModel,
- schema: fields,
- });
- // Add our class to alertify
- $(this.elements.body.childNodes[0]).addClass(
- 'alertify_tools_dialog_properties obj_properties'
- );
- // Render dialog
- view.render();
-
- this.elements.content.appendChild($container.get(0));
-
- var container = view.$el.find('.tab-content:first > .tab-pane.active:first');
- commonUtils.findAndSetFocus(container);
-
- // Listen to model & if filename is provided then enable Backup button
- this.view.model.on('change', function() {
- if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
- this.errorModel.clear();
- self.__internal.buttons[2].element.disabled = false;
- } else {
- self.__internal.buttons[2].element.disabled = true;
- this.errorModel.set('file', gettext('Please provide a filename'));
- }
- });
- },
- // Callback functions when click on the buttons of the Alertify dialogs
- callback: function(e) {
- // Fetch current server id
- var t = pgBrowser.tree,
- i = t.selected(),
- d = i && i.length == 1 ? t.itemData(i) : undefined,
- node = d && pgBrowser.Nodes[d._type];
-
- if (e.button.element.name == 'dialog_help' || e.button.element.name == 'object_help') {
- e.cancel = true;
- pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
- node, i, e.button.element.getAttribute('label'));
- return;
- }
-
- if (e.button['data-btn-name'] === 'backup') {
-
- if (!d)
- return;
-
- var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
-
- var self = this,
- baseUrl = url_for('backup.create_server_job', {
- 'sid': treeInfo.server._id,
- }),
- args = this.view.model.toJSON();
-
- $.ajax({
- url: baseUrl,
- method: 'POST',
- data: {
- 'data': JSON.stringify(args),
- },
- success: function(res) {
- if (res.success) {
- alertify.success(gettext('Backup job created.'), 5);
- pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
- } else {
- console.warn(res);
- }
- },
- error: function(xhr) {
- try {
- var err = $.parseJSON(xhr.responseText);
- alertify.alert(
- gettext('Backup job failed.'),
- err.errormsg
- );
- } catch (e) {
- console.warn(e.stack || e);
- }
- },
- });
- }
- },
- };
- });
- }
- alertify[DialogName](true).resizeTo('60%', '50%');
+ start_backup_global_server: function(action, treeItem, params) {
+ let dialog = new globalBackupDialog.BackupDialog(
+ pgBrowser,
+ $,
+ alertify,
+ BackupModel
+ );
+ dialog.draw(action, treeItem, params);
},
// Callback to draw Backup Dialog for objects
backup_objects: function(action, treeItem) {
-
- var i = treeItem || pgBrowser.tree.selected(),
- server_data = null;
-
- while (i) {
- var node_data = pgBrowser.tree.itemData(i);
- if (node_data._type == 'server') {
- server_data = node_data;
- break;
- }
-
- if (pgBrowser.tree.hasParent(i)) {
- i = $(pgBrowser.tree.parent(i));
- } else {
- alertify.alert(
- gettext('Backup Error'),
- gettext('Please select server or child node from tree.')
- );
- break;
- }
- }
-
- if (!server_data) {
- return;
- }
-
- var module = 'paths',
- preference_name = 'pg_bin_dir',
- msg = gettext('Please set binary path for PostgreSQL Server from preferences.');
-
- if ((server_data.type && server_data.type == 'ppas') ||
- server_data.server_type == 'ppas') {
- preference_name = 'ppas_bin_dir';
- msg = gettext('Please set binary path for EDB Postgres Advanced Server from preferences.');
- }
-
- var preference = pgBrowser.get_preference(module, preference_name);
-
- if (preference) {
- if (!preference.value) {
- alertify.alert(gettext('Configuration required'), msg);
- return;
- }
- } else {
- alertify.alert(
- gettext('Backup Error'),
- S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value()
- );
- return;
- }
-
- var title = S(gettext('Backup (%s: %s)')),
- tree = pgBrowser.tree,
- item = treeItem || tree.selected(),
- data = item && item.length == 1 && tree.itemData(item),
- node = data && data._type && pgBrowser.Nodes[data._type];
-
- if (!node)
- return;
-
- var treeInfo = node.getTreeNodeHierarchy.apply(node, [item]);
-
- if (treeInfo.database._label.indexOf('=') >= 0) {
- alertify.alert(
- gettext('Backup error'),
- gettext('Backup job creation failed. '+
- 'Databases with = symbols in the name cannot be backed up using this utility.')
- );
- return;
- }
-
- title = title.sprintf(node.label, data.label).value();
-
- if (!alertify.backup_objects) {
- // Create Dialog title on the fly with node details
- alertify.dialog('backup_objects', function factory() {
- return {
- main: function(title) {
- this.set('title', title);
- },
- build: function() {
- alertify.pgDialogBuild.apply(this);
- },
- setup: function() {
- return {
- buttons: [{
- text: '',
- className: 'btn btn-default pull-left fa fa-lg fa-info',
- attrs: {
- name: 'object_help',
- type: 'button',
- url: 'backup.html',
- label: gettext('Backup'),
- },
- }, {
- text: '',
- key: 112,
- className: 'btn btn-default pull-left fa fa-lg fa-question',
- attrs: {
- name: 'dialog_help',
- type: 'button',
- label: gettext('Backup'),
- url: url_for('help.static', {
- 'filename': 'backup_dialog.html',
- }),
- },
- }, {
- text: gettext('Backup'),
- key: 13,
- className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
- 'data-btn-name': 'backup',
- }, {
- text: gettext('Cancel'),
- key: 27,
- className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
- 'data-btn-name': 'cancel',
- }],
- // Set options for dialog
- options: {
- title: title,
- //disable both padding and overflow control.
- padding: !1,
- overflow: !1,
- model: 0,
- resizable: true,
- maximizable: true,
- pinnable: false,
- closableByDimmer: false,
- modal: false,
- },
- };
- },
- hooks: {
- // triggered when the dialog is closed
- onclose: function() {
- if (this.view) {
- this.view.remove({
- data: true,
- internal: true,
- silent: true,
- });
- }
- },
- },
- prepare: function() {
- var self = this;
- // Disable Backup button until user provides Filename
- this.__internal.buttons[2].element.disabled = true;
- var $container = $('<div class=\'backup_dialog\'></div>');
- var t = pgBrowser.tree,
- i = t.selected(),
- d = i && i.length == 1 ? t.itemData(i) : undefined,
- node = d && pgBrowser.Nodes[d._type];
-
- if (!d)
- return;
-
- var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
-
- var newModel = new BackupObjectModel({}, {
- node_info: treeInfo,
- }),
- fields = Backform.generateViewSchema(
- treeInfo, newModel, 'create', node, treeInfo.server, true
- );
-
- var view = this.view = new Backform.Dialog({
- el: $container,
- model: newModel,
- schema: fields,
- });
-
- $(this.elements.body.childNodes[0]).addClass(
- 'alertify_tools_dialog_properties obj_properties'
- );
-
- view.render();
-
- this.elements.content.appendChild($container.get(0));
-
- if(view) {
- view.$el.attr('tabindex', -1);
- // var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
- pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
- var container = view.$el.find('.tab-content:first > .tab-pane.active:first');
- commonUtils.findAndSetFocus(container);
- }
- // Listen to model & if filename is provided then enable Backup button
- this.view.model.on('change', function() {
- if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
- this.errorModel.clear();
- self.__internal.buttons[2].element.disabled = false;
- } else {
- self.__internal.buttons[2].element.disabled = true;
- this.errorModel.set('file', gettext('Please provide filename'));
- }
- });
-
- },
- // Callback functions when click on the buttons of the Alertify dialogs
- callback: function(e) {
- // Fetch current server id
- var t = pgBrowser.tree,
- i = t.selected(),
- d = i && i.length == 1 ? t.itemData(i) : undefined,
- node = d && pgBrowser.Nodes[d._type];
-
- if (e.button.element.name == 'dialog_help' || e.button.element.name == 'object_help') {
- e.cancel = true;
- pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
- node, i, e.button.element.getAttribute('label'));
- return;
- }
-
- if (e.button['data-btn-name'] === 'backup') {
- if (!d)
- return;
-
- var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
-
- // Set current database into model
- this.view.model.set('database', treeInfo.database._label);
-
- // We will remove once object tree is implemented
- // If selected node is Schema then add it in model
- if (d._type == 'schema') {
- var schemas = [];
- schemas.push(d._label);
- this.view.model.set('schemas', schemas);
- }
- // If selected node is Table then add it in model along with
- // its schema
- if (d._type == 'table') {
- this.view.model.set(
- 'tables', [
- [treeInfo.schema._label, d._label],
- ]
- );
- }
-
- // Remove ratio attribute from model if it has empty string.
- // The valid value can be between 0 to 9.
- if (_.isEmpty(this.view.model.get('ratio'))) {
- this.view.model.unset('ratio');
- }
-
- var self = this,
- baseUrl = url_for('backup.create_object_job', {
- 'sid': treeInfo.server._id,
- }),
- args = this.view.model.toJSON();
-
- $.ajax({
- url: baseUrl,
- method: 'POST',
- data: {
- 'data': JSON.stringify(args),
- },
- success: function(res) {
- if (res.success) {
- alertify.success(gettext('Backup job created.'), 5);
- pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
- }
- },
- error: function(xhr) {
- try {
- var err = $.parseJSON(xhr.responseText);
- alertify.alert(
- gettext('Backup job failed.'),
- err.errormsg
- );
- } catch (e) {
- console.warn(e.stack || e);
- }
- },
- });
- }
- },
- };
- });
- }
- alertify.backup_objects(title).resizeTo('65%', '60%');
+ let dialog = new globalBackupDialog.BackupDialog(
+ pgBrowser,
+ $,
+ alertify,
+ BackupObjectModel
+ );
+ dialog.draw(action, treeItem, null);
},
};
return pgBrowser.Backup;
diff --git a/web/pgadmin/tools/backup/static/js/backup_dialog.js b/web/pgadmin/tools/backup/static/js/backup_dialog.js
new file mode 100644
index 00000000..c74c376c
--- /dev/null
+++ b/web/pgadmin/tools/backup/static/js/backup_dialog.js
@@ -0,0 +1,69 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from '../../../../static/js/gettext';
+import Backform from '../../../../static/js/backform.pgadmin';
+import {Dialog} from '../../../../static/js/alertify/dialog';
+
+export class BackupDialog extends Dialog {
+ constructor(pgBrowser, $, alertify, BackupModel, backform = Backform) {
+ super('Backup Error',
+ '<div class=\'backup_dialog\'></div>',
+ pgBrowser, $, alertify, BackupModel, backform);
+ }
+
+ draw(action, aciTreeItem, params) {
+ const serverInformation = this.retrieveAncestorOfTypeServer(aciTreeItem);
+
+ if (!serverInformation) {
+ return;
+ }
+
+ if (!this.hasBinariesConfiguration(serverInformation)) {
+ return;
+ }
+
+ const typeOfDialog = BackupDialog.typeOfDialog(params);
+
+ if (!this.canExecuteOnCurrentDatabase(aciTreeItem)) {
+ return;
+ }
+
+ const dialog = this.createOrGetDialog(BackupDialog.dialogTitle(typeOfDialog),
+ typeOfDialog);
+ dialog(true).resizeTo('60%', '50%');
+ }
+
+ static typeOfDialog(params) {
+ if (params === null) {
+ return 'backup_objects';
+ }
+ let typeOfDialog = 'server';
+ if (!_.isUndefined(params['globals']) && params['globals']) {
+ typeOfDialog = 'globals';
+ }
+ return typeOfDialog;
+ }
+
+ static dialogTitle(typeOfDialog) {
+ if (typeOfDialog === 'backup_objects') {
+ return null;
+ }
+ return ((typeOfDialog === 'globals') ?
+ gettext('Backup Globals...') :
+ gettext('Backup Server...'));
+ }
+
+ dialogName(typeOfDialog) {
+ if (typeOfDialog === 'backup_objects') {
+ return typeOfDialog;
+ }
+ return 'BackupDialog_' + typeOfDialog;
+ }
+}
diff --git a/web/pgadmin/tools/backup/static/js/backup_dialog_wrapper.js b/web/pgadmin/tools/backup/static/js/backup_dialog_wrapper.js
new file mode 100644
index 00000000..2cebe3d1
--- /dev/null
+++ b/web/pgadmin/tools/backup/static/js/backup_dialog_wrapper.js
@@ -0,0 +1,258 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {getTreeNodeHierarchyFromElement} from '../../../../static/js/tree/pgadmin_tree_node';
+import axios from 'axios/index';
+import gettext from '../../../../static/js/gettext';
+import url_for from '../../../../static/js/url_for';
+import _ from 'underscore';
+import {DialogWrapper} from '../../../../static/js/alertify/dialog_wrapper';
+
+export class BackupDialogWrapper extends DialogWrapper {
+ constructor(dialogContainerSelector, dialogTitle, typeOfDialog,
+ jquery, pgBrowser, alertify, dialogModel, backform) {
+ super(dialogContainerSelector, dialogTitle, jquery,
+ pgBrowser, alertify, dialogModel, backform);
+ this.typeOfDialog = typeOfDialog;
+ }
+
+ main(title) {
+ this.set('title', title);
+ }
+
+ setup() {
+ return {
+ buttons: [{
+ text: '',
+ className: 'btn btn-default pull-left fa fa-lg fa-info',
+ attrs: {
+ name: 'object_help',
+ type: 'button',
+ url: 'backup.html',
+ label: gettext('Backup'),
+ },
+ }, {
+ text: '',
+ key: 112,
+ className: 'btn btn-default pull-left fa fa-lg fa-question',
+ attrs: {
+ name: 'dialog_help',
+ type: 'button',
+ label: gettext('Backup'),
+ url: url_for('help.static', {
+ 'filename': 'backup_dialog.html',
+ }),
+ },
+ }, {
+ text: gettext('Backup'),
+ key: 13,
+ className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
+ 'data-btn-name': 'backup',
+ }, {
+ text: gettext('Cancel'),
+ key: 27,
+ className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+ 'data-btn-name': 'cancel',
+ }],
+ // Set options for dialog
+ options: {
+ title: this.dialogTitle,
+ //disable both padding and overflow control.
+ padding: !1,
+ overflow: !1,
+ model: 0,
+ resizable: true,
+ maximizable: true,
+ pinnable: false,
+ closableByDimmer: false,
+ modal: false,
+ },
+ };
+ }
+
+ prepare() {
+ this.disableBackupButton();
+
+ const $container = this.jquery(this.dialogContainerSelector);
+ const selectedTreeNode = this.getSelectedNode();
+ const selectedTreeNodeData = this.getSelectedNodeData(selectedTreeNode);
+ if (!selectedTreeNodeData) {
+ return;
+ }
+
+ const node = this.pgBrowser.Nodes[selectedTreeNodeData._type];
+ if (this.dialogTitle === null) {
+ const title = `Backup (${node.label}: ${selectedTreeNodeData.label})`;
+ this.main(title);
+ }
+
+ const treeInfo = getTreeNodeHierarchyFromElement(this.pgBrowser, selectedTreeNode);
+ const dialog = this.createDialog(node, treeInfo, this.typeOfDialog, $container);
+ this.addAlertifyClassToBackupNodeChildNodes();
+ dialog.render();
+
+ this.elements.content.appendChild($container.get(0));
+
+ this.focusOnDialog(dialog);
+ this.setListenersForFilenameChanges();
+ }
+
+ callback(event) {
+ const selectedTreeNode = this.getSelectedNode();
+ const selectedTreeNodeData = this.getSelectedNodeData(selectedTreeNode);
+ const node = selectedTreeNodeData && this.pgBrowser.Nodes[selectedTreeNodeData._type];
+
+ if (this.wasHelpButtonPressed(event)) {
+ event.cancel = true;
+ this.pgBrowser.showHelp(
+ event.button.element.name,
+ event.button.element.getAttribute('url'),
+ node,
+ selectedTreeNode,
+ event.button.element.getAttribute('label')
+ );
+ return;
+ }
+
+ if (this.wasBackupButtonPressed(event)) {
+
+ if (!selectedTreeNodeData)
+ return;
+
+ const serverIdentifier = this.retrieveServerIdentifier(node, selectedTreeNode);
+
+ const dialog = this;
+ let urlShortcut = 'backup.create_server_job';
+ if (this.typeOfDialog === 'backup_objects') {
+ urlShortcut = 'backup.create_object_job';
+ }
+ const baseUrl = url_for(urlShortcut, {
+ 'sid': serverIdentifier,
+ });
+
+ const treeInfo = getTreeNodeHierarchyFromElement(
+ this.pgBrowser,
+ selectedTreeNode
+ );
+
+ this.setExtraParameters(selectedTreeNode, treeInfo);
+
+ let service = axios.create({});
+ service.post(
+ baseUrl,
+ this.view.model.toJSON()
+ ).then(function () {
+ dialog.alertify.success(gettext('Backup job created.'), 5);
+ dialog.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialog);
+ }).catch(function (error) {
+ try {
+ const err = error.response.data;
+ dialog.alertify.alert(
+ gettext('Backup job failed.'),
+ err.errormsg
+ );
+ } catch (e) {
+ console.warn(e.stack || e);
+ }
+ });
+ }
+ }
+
+ addAlertifyClassToBackupNodeChildNodes() {
+ this.jquery(this.elements.body.childNodes[0]).addClass(
+ 'alertify_tools_dialog_properties obj_properties'
+ );
+ }
+
+ getSelectedNode() {
+ const tree = this.pgBrowser.treeMenu;
+ const selectedNode = tree.selected();
+ if (selectedNode) {
+ return tree.findNodeByDomElement(selectedNode);
+ } else {
+ return undefined;
+ }
+ }
+
+ disableBackupButton() {
+ this.__internal.buttons[2].element.disabled = true;
+ }
+
+ enableBackupButton() {
+ this.__internal.buttons[2].element.disabled = false;
+ }
+
+ createDialog(node, treeInfo, typeOfDialog, $container) {
+ let attributes = {};
+ if (typeOfDialog !== 'backup_objects') {
+ attributes['type'] = typeOfDialog;
+ }
+ // Instance of backbone model
+ const newModel = new this.dialogModel(attributes, {
+ node_info: treeInfo,
+ });
+ const fields = this.backform.generateViewSchema(
+ treeInfo, newModel, 'create', node, treeInfo.server, true
+ );
+
+ return this.view = new this.backform.Dialog({
+ el: $container,
+ model: newModel,
+ schema: fields,
+ });
+ }
+
+ retrieveServerIdentifier(node, selectedTreeNode) {
+ const treeInfo = getTreeNodeHierarchyFromElement(
+ this.pgBrowser,
+ selectedTreeNode
+ );
+ return treeInfo.server._id;
+ }
+
+ setListenersForFilenameChanges() {
+ const self = this;
+
+ this.view.model.on('change', function () {
+ if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
+ this.errorModel.clear();
+ self.enableBackupButton();
+ } else {
+ self.disableBackupButton();
+ this.errorModel.set('file', gettext('Please provide a filename'));
+ }
+ });
+ }
+
+ setExtraParameters(selectedTreeNode, treeInfo) {
+ if (this.typeOfDialog === 'backup_objects') {
+
+ this.view.model.set('database', treeInfo.database._label);
+
+ const nodeData = selectedTreeNode.getData();
+ if (nodeData._type === 'schema') {
+ this.view.model.set('schemas', [nodeData._label]);
+ }
+
+ if (nodeData._type === 'table') {
+ this.view.model.set('tables', [
+ [treeInfo.schema._label, nodeData._label],
+ ]);
+ }
+
+ if (_.isEmpty(this.view.model.get('ratio'))) {
+ this.view.model.unset('ratio');
+ }
+ }
+ }
+
+ wasBackupButtonPressed(event) {
+ return event.button['data-btn-name'] === 'backup';
+ }
+}
diff --git a/web/pgadmin/tools/backup/static/js/menu_utils.js b/web/pgadmin/tools/backup/static/js/menu_utils.js
new file mode 100644
index 00000000..47a9f0d3
--- /dev/null
+++ b/web/pgadmin/tools/backup/static/js/menu_utils.js
@@ -0,0 +1,23 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {isValidTreeNodeData} from '../../../../static/js/tree/tree';
+
+export const backupSupportedNodes = [
+ 'database', 'schema', 'table', 'partition',
+];
+
+function isNodeAServerAndConnected(treeNodeData) {
+ return (('server' === treeNodeData._type) && treeNodeData.connected);
+}
+
+export function menuEnabledServer(treeNodeData) {
+ return isValidTreeNodeData(treeNodeData)
+ && isNodeAServerAndConnected(treeNodeData);
+}
diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid.js b/web/pgadmin/tools/datagrid/static/js/datagrid.js
index b0ed60f6..520a9ce5 100644
--- a/web/pgadmin/tools/datagrid/static/js/datagrid.js
+++ b/web/pgadmin/tools/datagrid/static/js/datagrid.js
@@ -1,10 +1,14 @@
define('pgadmin.datagrid', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'pgadmin.alertifyjs', 'sources/pgadmin', 'bundled_codemirror',
- 'sources/sqleditor_utils', 'backbone', 'wcdocker',
+ 'sources/sqleditor_utils', 'backbone',
+ 'tools/datagrid/static/js/show_data',
+ 'tools/datagrid/static/js/get_panel_title',
+ 'tools/datagrid/static/js/show_query_tool',
+ 'wcdocker',
], function(
gettext, url_for, $, _, alertify, pgAdmin, codemirror, sqlEditorUtils,
- Backbone
+ Backbone, showData, panelTitle, showQueryTool
) {
// Some scripts do export their object in the window only.
// Generally the one, which do no have AMD support.
@@ -161,55 +165,7 @@ define('pgadmin.datagrid', [
// This is a callback function to show data when user click on menu item.
show_data_grid: function(data, i) {
- var self = this,
- d = pgAdmin.Browser.tree.itemData(i);
- if (d === undefined) {
- alertify.alert(
- gettext('Data Grid Error'),
- gettext('No object selected.')
- );
- return;
- }
-
- // Get the parent data from the tree node hierarchy.
- var node = pgBrowser.Nodes[d._type],
- parentData = node.getTreeNodeHierarchy(i);
-
- // If server, database or schema is undefined then return from the function.
- if (parentData.server === undefined || parentData.database === undefined) {
- return;
- }
- // If schema, view, catalog object all are undefined then return from the function.
- if (parentData.schema === undefined && parentData.view === undefined &&
- parentData.catalog === undefined) {
- return;
- }
-
- var nsp_name = '';
-
- if (parentData.schema != undefined) {
- nsp_name = parentData.schema.label;
- }
- else if (parentData.view != undefined) {
- nsp_name = parentData.view.label;
- }
- else if (parentData.catalog != undefined) {
- nsp_name = parentData.catalog.label;
- }
- var url_params = {
- 'cmd_type': data.mnuid,
- 'obj_type': d._type,
- 'sgid': parentData.server_group._id,
- 'sid': parentData.server._id,
- 'did': parentData.database._id,
- 'obj_id': d._id,
- };
-
- var baseUrl = url_for('datagrid.initialize_datagrid', url_params);
- var grid_title = parentData.server.label + ' - ' + parentData.database.label + ' - '
- + nsp_name + '.' + d.label;
-
- self.create_transaction(baseUrl, null, 'false', parentData.server.server_type, '', grid_title, '');
+ showData.showDataGrid(this, pgBrowser, alertify, data, i);
},
// This is a callback function to show filtered data when user click on menu item.
@@ -384,63 +340,12 @@ define('pgadmin.datagrid', [
},
get_panel_title: function() {
- // Get the parent data from the tree node hierarchy.
- var tree = pgAdmin.Browser.tree,
- selected_item = tree.selected(),
- item_data = tree.itemData(selected_item);
-
- var node = pgBrowser.Nodes[item_data._type],
- parentData = node.getTreeNodeHierarchy(selected_item);
-
- // If server, database is undefined then return from the function.
- if (parentData.server === undefined) {
- return;
- }
- // If Database is not available then use default db
- var db_label = parentData.database ? parentData.database.label
- : parentData.server.db;
-
- var grid_title = db_label + ' on ' + parentData.server.user.name + '@' +
- parentData.server.label;
- return grid_title;
+ return panelTitle.getPanelTitle(pgBrowser);
},
// This is a callback function to show query tool when user click on menu item.
- show_query_tool: function(url, i, panel_title) {
- var sURL = url || '',
- d = pgAdmin.Browser.tree.itemData(i);
-
- panel_title = panel_title || '';
- if (d === undefined) {
- alertify.alert(
- gettext('Query Tool Error'),
- gettext('No object selected.')
- );
- return;
- }
-
- // Get the parent data from the tree node hierarchy.
- var node = pgBrowser.Nodes[d._type],
- parentData = node.getTreeNodeHierarchy(i);
-
- // If server, database is undefined then return from the function.
- if (parentData.server === undefined) {
- return;
- }
-
- var url_params = {
- 'sgid': parentData.server_group._id,
- 'sid': parentData.server._id,
- },
- url_endpoint = 'datagrid.initialize_query_tool';
- // If database not present then use Maintenance database
- // We will handle this at server side
- if (parentData.database) {
- url_params['did'] = parentData.database._id;
- url_endpoint = 'datagrid.initialize_query_tool_with_did';
- }
- var baseUrl = url_for(url_endpoint, url_params);
-
- this.create_transaction(baseUrl, null, 'true', parentData.server.server_type, sURL, panel_title, '', false);
+ show_query_tool: function(url, aciTreeIdentifier, panelTitle) {
+ showQueryTool.showQueryTool(this, pgBrowser, alertify, url,
+ aciTreeIdentifier, panelTitle);
},
create_transaction: function(baseUrl, target, is_query_tool, server_type, sURL, panel_title, sql_filter, recreate) {
var self = this;
diff --git a/web/pgadmin/tools/datagrid/static/js/get_panel_title.js b/web/pgadmin/tools/datagrid/static/js/get_panel_title.js
new file mode 100644
index 00000000..64b3a3d2
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/static/js/get_panel_title.js
@@ -0,0 +1,33 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {getTreeNodeHierarchyFromIdentifier} from '../../../../static/js/tree/pgadmin_tree_node';
+
+function getDatabaseLabel(parentData) {
+ return parentData.database ? parentData.database.label
+ : parentData.server.db;
+}
+
+function isServerInformationAvailable(parentData) {
+ return parentData.server === undefined;
+}
+
+export function getPanelTitle(pgBrowser) {
+ const selected_item = pgBrowser.treeMenu.selected();
+
+ const parentData = getTreeNodeHierarchyFromIdentifier
+ .call(pgBrowser, selected_item);
+ if (isServerInformationAvailable(parentData)) {
+ return;
+ }
+
+ const db_label = getDatabaseLabel(parentData);
+
+ return `${db_label} on ${parentData.server.user.name}@${parentData.server.label}`;
+}
diff --git a/web/pgadmin/tools/datagrid/static/js/show_data.js b/web/pgadmin/tools/datagrid/static/js/show_data.js
new file mode 100644
index 00000000..373c97cd
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/static/js/show_data.js
@@ -0,0 +1,92 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import gettext from '../../../../static/js/gettext';
+import url_for from '../../../../static/js/url_for';
+import {getTreeNodeHierarchyFromIdentifier} from '../../../../static/js/tree/pgadmin_tree_node';
+
+export function showDataGrid(
+ datagrid,
+ pgBrowser,
+ alertify,
+ connectionData,
+ aciTreeIdentifier
+) {
+ const node = pgBrowser.treeMenu.findNodeByDomElement(aciTreeIdentifier);
+ if (node === undefined || !node.getData()) {
+ alertify.alert(
+ gettext('Data Grid Error'),
+ gettext('No object selected.')
+ );
+ return;
+ }
+
+ const parentData = getTreeNodeHierarchyFromIdentifier.call(
+ pgBrowser,
+ aciTreeIdentifier
+ );
+
+ if (hasServerOrDatabaseConfiguration(parentData)
+ || !hasSchemaOrCatalogOrViewInformation(parentData)) {
+ return;
+ }
+
+ let namespaceName = retrieveNameSpaceName(parentData);
+ const baseUrl = generateUrl(connectionData, node.getData(), parentData);
+ const grid_title = generateDatagridTitle(parentData, namespaceName, node.getData());
+
+ datagrid.create_transaction(
+ baseUrl,
+ null,
+ 'false',
+ parentData.server.server_type,
+ '',
+ grid_title,
+ ''
+ );
+}
+
+
+function retrieveNameSpaceName(parentData) {
+ if (parentData.schema !== undefined) {
+ return parentData.schema.label;
+ }
+ else if (parentData.view !== undefined) {
+ return parentData.view.label;
+ }
+ else if (parentData.catalog !== undefined) {
+ return parentData.catalog.label;
+ }
+ return '';
+}
+
+function generateUrl(connectionData, nodeData, parentData) {
+ const url_params = {
+ 'cmd_type': connectionData.mnuid,
+ 'obj_type': nodeData._type,
+ 'sgid': parentData.server_group._id,
+ 'sid': parentData.server._id,
+ 'did': parentData.database._id,
+ 'obj_id': nodeData._id,
+ };
+
+ return url_for('datagrid.initialize_datagrid', url_params);
+}
+
+function hasServerOrDatabaseConfiguration(parentData) {
+ return parentData.server === undefined || parentData.database === undefined;
+}
+
+function hasSchemaOrCatalogOrViewInformation(parentData) {
+ return parentData.schema !== undefined || parentData.view !== undefined ||
+ parentData.catalog !== undefined;
+}
+
+function generateDatagridTitle(parentData, namespaceName, nodeData) {
+ return `${parentData.server.label} - ${parentData.database.label} - ${namespaceName}.${nodeData.label}`;
+}
diff --git a/web/pgadmin/tools/datagrid/static/js/show_query_tool.js b/web/pgadmin/tools/datagrid/static/js/show_query_tool.js
new file mode 100644
index 00000000..0eb12b8b
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/static/js/show_query_tool.js
@@ -0,0 +1,63 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from '../../../../static/js/gettext';
+import url_for from '../../../../static/js/url_for';
+import {getTreeNodeHierarchyFromIdentifier} from '../../../../static/js/tree/pgadmin_tree_node';
+
+function hasDatabaseInformation(parentData) {
+ return parentData.database;
+}
+
+function generateUrl(parentData) {
+ let url_endpoint = 'datagrid.initialize_query_tool';
+ let url_params = {
+ 'sgid': parentData.server_group._id,
+ 'sid': parentData.server._id,
+ };
+
+ if (hasDatabaseInformation(parentData)) {
+ url_params['did'] = parentData.database._id;
+ url_endpoint = 'datagrid.initialize_query_tool_with_did';
+ }
+
+ return url_for(url_endpoint, url_params);
+}
+
+function hasServerInformations(parentData) {
+ return parentData.server === undefined;
+}
+
+export function showQueryTool(datagrid, pgBrowser, alertify, url,
+ aciTreeIdentifier, panelTitle) {
+ const sURL = url || '';
+ const queryToolTitle = panelTitle || '';
+
+ const currentNode = pgBrowser.treeMenu.findNodeByDomElement(aciTreeIdentifier);
+ if (currentNode === undefined) {
+ alertify.alert(
+ gettext('Query Tool Error'),
+ gettext('No object selected.')
+ );
+ return;
+ }
+
+ const parentData = getTreeNodeHierarchyFromIdentifier.call(
+ pgBrowser, aciTreeIdentifier);
+
+ if (hasServerInformations(parentData)) {
+ return;
+ }
+
+ const baseUrl = generateUrl(parentData);
+
+ datagrid.create_transaction(
+ baseUrl, null, 'true',
+ parentData.server.server_type, sURL, queryToolTitle, '', false);
+}
diff --git a/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js b/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js
index 750887ec..753813f0 100644
--- a/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js
+++ b/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js
@@ -2,12 +2,15 @@
define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'pgadmin.alertifyjs', 'pgadmin.backgrid', 'pgadmin.backform',
- 'pgadmin.browser', 'pgadmin.browser.node', 'backgrid.select.all',
+ 'pgadmin.browser', 'pgadmin.browser.node',
+ 'tools/grant_wizard/static/js/menu_utils',
+ 'sources/nodes/supported_database_node',
+ 'backgrid.select.all',
'backgrid.filter', 'pgadmin.browser.server.privilege',
'pgadmin.browser.wizard',
], function(
gettext, url_for, $, _, Backbone, Alertify, Backgrid, Backform, pgBrowser,
- pgNode
+ pgNode, menuUtils, supportedNodes
) {
// if module is already initialized, refer to that.
@@ -143,41 +146,6 @@ define([
this.initialized = true;
- // Define list of nodes on which grant wizard context menu option appears
- var supported_nodes = [
- 'schema', 'coll-function', 'coll-sequence',
- 'coll-table', 'coll-view', 'coll-procedure',
- 'coll-mview', 'database', 'coll-trigger_function',
- ],
-
- /**
- Enable/disable grantwizard menu in tools based
- on node selected
- if selected node is present in supported_nodes,
- menu will be enabled otherwise disabled.
- Also, hide it for system view in catalogs
- */
- menu_enabled = function(itemData, item) {
- var t = pgBrowser.tree,
- i = item,
- d = itemData;
- var parent_item = t.hasParent(i) ? t.parent(i) : null,
- parent_data = parent_item ? t.itemData(parent_item) : null;
- if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
- if (_.indexOf(supported_nodes, d._type) !== -1 &&
- parent_data._type != 'catalog') {
- if (d._type == 'database' && d.allowConn)
- return true;
- else if (d._type != 'database')
- return true;
- else
- return false;
- } else
- return false;
- } else
- return false;
- };
-
// Define the nodes on which the menus to be appear
var menus = [{
name: 'grant_wizard_schema',
@@ -187,21 +155,25 @@ define([
priority: 14,
label: gettext('Grant Wizard...'),
icon: 'fa fa-unlock-alt',
- enable: menu_enabled,
+ enable: supportedNodes.enabled.bind(
+ null, pgBrowser.treeMenu, menuUtils.supportedNodes
+ ),
}];
// Add supported menus into the menus list
- for (var idx = 0; idx < supported_nodes.length; idx++) {
+ for (var idx = 0; idx < menuUtils.supportedNodes.length; idx++) {
menus.push({
- name: 'grant_wizard_schema_context_' + supported_nodes[idx],
- node: supported_nodes[idx],
+ name: 'grant_wizard_schema_context_' + menuUtils.supportedNodes[idx],
+ node: menuUtils.supportedNodes[idx],
module: this,
applies: ['context'],
callback: 'start_grant_wizard',
priority: 14,
label: gettext('Grant Wizard...'),
icon: 'fa fa-unlock-alt',
- enable: menu_enabled,
+ enable: supportedNodes.enabled.bind(
+ null, pgBrowser.treeMenu, menuUtils.supportedNodes
+ ),
});
}
pgBrowser.add_menus(menus);
diff --git a/web/pgadmin/tools/grant_wizard/static/js/menu_utils.js b/web/pgadmin/tools/grant_wizard/static/js/menu_utils.js
new file mode 100644
index 00000000..b56ce3cd
--- /dev/null
+++ b/web/pgadmin/tools/grant_wizard/static/js/menu_utils.js
@@ -0,0 +1,16 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+export const supportedNodes = [
+ 'schema', 'coll-function', 'coll-sequence',
+ 'coll-table', 'coll-view', 'coll-procedure',
+ 'coll-mview', 'database', 'coll-trigger_function',
+];
+
+
diff --git a/web/pgadmin/tools/import_export/static/js/import_export.js b/web/pgadmin/tools/import_export/static/js/import_export.js
index 3058f122..f9e9ac7b 100644
--- a/web/pgadmin/tools/import_export/static/js/import_export.js
+++ b/web/pgadmin/tools/import_export/static/js/import_export.js
@@ -1,10 +1,12 @@
define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'pgadmin.alertifyjs',
'sources/pgadmin', 'pgadmin.browser', 'backbone', 'backgrid', 'backform',
- 'sources/utils', 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui',
+ 'sources/utils',
+ 'sources/nodes/supported_database_node',
+ 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui',
], function(
gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
-Backform, commonUtils
+Backform, commonUtils, supportedNodes
) {
pgAdmin = pgAdmin || window.pgAdmin || {};
@@ -383,25 +385,6 @@ Backform, commonUtils
this.initialized = true;
- /*
- * Enable/disable import menu in tools based on node selected. Import
- * menu will be enabled only when user select table node.
- */
- var menu_enabled = function(itemData, item) {
- var t = pgBrowser.tree,
- i = item,
- d = itemData;
- var parent_item = t.hasParent(i) ? t.parent(i) : null,
- parent_data = parent_item ? t.itemData(parent_item) : null;
- if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data))
- return (
- (_.indexOf(['table'], d._type) !== -1 &&
- parent_data._type != 'catalog') ? true : false
- );
- else
- return false;
- };
-
// Initialize the context menu to display the import options when user open the context menu for table
pgBrowser.add_menus([{
name: 'import',
@@ -413,7 +396,9 @@ Backform, commonUtils
priority: 10,
label: gettext('Import/Export...'),
icon: 'fa fa-shopping-cart',
- enable: menu_enabled,
+ enable: supportedNodes.enabled.bind(
+ null, pgBrowser.treeMenu, ['table']
+ ),
}]);
},
diff --git a/web/pgadmin/tools/maintenance/static/js/maintenance.js b/web/pgadmin/tools/maintenance/static/js/maintenance.js
index f2102602..df05c3d5 100644
--- a/web/pgadmin/tools/maintenance/static/js/maintenance.js
+++ b/web/pgadmin/tools/maintenance/static/js/maintenance.js
@@ -2,11 +2,14 @@ define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'pgadmin.alertifyjs', 'sources/pgadmin', 'pgadmin.browser', 'backbone',
'backgrid', 'backform', 'sources/utils',
+ 'tools/maintenance/static/js/menu_utils',
+ 'sources/nodes/supported_database_node',
'pgadmin.backform', 'pgadmin.backgrid',
'pgadmin.browser.node.ui',
], function(
gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
- Backform, commonUtils
+ Backform, commonUtils,
+ menuUtils, supportedNodes
) {
pgAdmin = pgAdmin || window.pgAdmin || {};
@@ -168,36 +171,6 @@ define([
this.initialized = true;
- var maintenance_supported_nodes = [
- 'database', 'table', 'primary_key',
- 'unique_constraint', 'index', 'partition',
- ];
-
- /**
- Enable/disable Maintenance menu in tools based on node selected.
- Maintenance menu will be enabled only when user select table and database node.
- */
- var menu_enabled = function(itemData, item) {
- var t = pgBrowser.tree,
- i = item,
- d = itemData;
- var parent_item = t.hasParent(i) ? t.parent(i) : null,
- parent_data = parent_item ? t.itemData(parent_item) : null;
- if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
- if (_.indexOf(maintenance_supported_nodes, d._type) !== -1 &&
- parent_data._type != 'catalog') {
- if (d._type == 'database' && d.allowConn)
- return true;
- else if (d._type != 'database')
- return true;
- else
- return false;
- } else
- return false;
- } else
- return false;
- };
-
var menus = [{
name: 'maintenance',
module: this,
@@ -206,21 +179,25 @@ define([
priority: 10,
label: gettext('Maintenance...'),
icon: 'fa fa-wrench',
- enable: menu_enabled,
+ enable: supportedNodes.enabled.bind(
+ null, pgBrowser.treeMenu, menuUtils.maintenanceSupportedNodes
+ ),
}];
// Add supported menus into the menus list
- for (var idx = 0; idx < maintenance_supported_nodes.length; idx++) {
+ for (var idx = 0; idx < menuUtils.maintenanceSupportedNodes.length; idx++) {
menus.push({
- name: 'maintenance_context_' + maintenance_supported_nodes[idx],
- node: maintenance_supported_nodes[idx],
+ name: 'maintenance_context_' + menuUtils.maintenanceSupportedNodes[idx],
+ node: menuUtils.maintenanceSupportedNodes[idx],
module: this,
applies: ['context'],
callback: 'callback_maintenance',
priority: 10,
label: gettext('Maintenance...'),
icon: 'fa fa-wrench',
- enable: menu_enabled,
+ enable: supportedNodes.enabled.bind(
+ null, pgBrowser.treeMenu, menuUtils.maintenanceSupportedNodes
+ ),
});
}
pgBrowser.add_menus(menus);
diff --git a/web/pgadmin/tools/maintenance/static/js/menu_utils.js b/web/pgadmin/tools/maintenance/static/js/menu_utils.js
new file mode 100644
index 00000000..8cde1baa
--- /dev/null
+++ b/web/pgadmin/tools/maintenance/static/js/menu_utils.js
@@ -0,0 +1,13 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+export const maintenanceSupportedNodes = [
+ 'database', 'table', 'primary_key',
+ 'unique_constraint', 'index', 'partition',
+];
diff --git a/web/pgadmin/tools/restore/static/js/menu_utils.js b/web/pgadmin/tools/restore/static/js/menu_utils.js
new file mode 100644
index 00000000..2d35c951
--- /dev/null
+++ b/web/pgadmin/tools/restore/static/js/menu_utils.js
@@ -0,0 +1,18 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+export const restoreSupportedNodes = [
+ 'database',
+ 'schema',
+ 'table',
+ 'function',
+ 'trigger',
+ 'index',
+ 'partition',
+];
diff --git a/web/pgadmin/tools/restore/static/js/restore.js b/web/pgadmin/tools/restore/static/js/restore.js
index 585b9729..3bde6de7 100644
--- a/web/pgadmin/tools/restore/static/js/restore.js
+++ b/web/pgadmin/tools/restore/static/js/restore.js
@@ -1,11 +1,13 @@
-// Restore dialog
define('tools.restore', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'underscore.string', 'pgadmin.alertifyjs', 'pgadmin.browser',
'pgadmin.backgrid', 'pgadmin.backform', 'sources/utils',
+ 'tools/restore/static/js/menu_utils',
+ 'sources/nodes/supported_database_node',
+ 'tools/restore/static/js/restore_dialog',
], function(
gettext, url_for, $, _, Backbone, S, alertify, pgBrowser, Backgrid, Backform,
-commonUtils
+commonUtils, menuUtils, supportedNodes, restoreDialog
) {
// if module is already initialized, refer to that.
@@ -307,59 +309,6 @@ commonUtils
this.initialized = true;
- // Define list of nodes on which restore context menu option appears
- var restore_supported_nodes = [
- 'database', 'schema',
- 'table', 'function',
- 'trigger', 'index',
- 'partition',
- ];
-
- /**
- Enable/disable restore menu in tools based
- on node selected
- if selected node is present in supported_nodes,
- menu will be enabled otherwise disabled.
- Also, hide it for system view in catalogs
- */
- var menu_enabled = function(itemData, item, data) {
- var t = pgBrowser.tree,
- i = item,
- d = itemData;
- var parent_item = t.hasParent(i) ? t.parent(i) : null,
- parent_data = parent_item ? t.itemData(parent_item) : null;
- if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
- if (_.indexOf(restore_supported_nodes, d._type) !== -1 &&
- is_parent_catalog(itemData, item, data)) {
- if (d._type == 'database' && d.allowConn)
- return true;
- else if (d._type != 'database')
- return true;
- else
- return false;
- } else
- return false;
- } else
- return false;
- };
-
- var is_parent_catalog = function(itemData, item) {
- var t = pgBrowser.tree,
- i = item,
- d = itemData;
-
- // To iterate over tree to check parent node
- while (i) {
- // If it is schema then allow user to restore
- if (_.indexOf(['catalog'], d._type) > -1)
- return false;
- i = t.hasParent(i) ? t.parent(i) : null;
- d = i ? t.itemData(i) : null;
- }
- // by default we do not want to allow create menu
- return true;
- };
-
// Define the nodes on which the menus to be appear
var menus = [{
name: 'restore_object',
@@ -369,20 +318,24 @@ commonUtils
priority: 13,
label: gettext('Restore...'),
icon: 'fa fa-upload',
- enable: menu_enabled,
+ enable: supportedNodes.enabled.bind(
+ null, pgBrowser.treeMenu, menuUtils.restoreSupportedNodes
+ ),
}];
- for (var idx = 0; idx < restore_supported_nodes.length; idx++) {
+ for (var idx = 0; idx < menuUtils.restoreSupportedNodes.length; idx++) {
menus.push({
- name: 'restore_' + restore_supported_nodes[idx],
- node: restore_supported_nodes[idx],
+ name: 'restore_' + menuUtils.restoreSupportedNodes[idx],
+ node: menuUtils.restoreSupportedNodes[idx],
module: this,
applies: ['context'],
callback: 'restore_objects',
priority: 13,
label: gettext('Restore...'),
icon: 'fa fa-upload',
- enable: menu_enabled,
+ enable: supportedNodes.enabled.bind(
+ null, pgBrowser.treeMenu, menuUtils.restoreSupportedNodes
+ ),
});
}
@@ -391,318 +344,8 @@ commonUtils
},
// Callback to draw Backup Dialog for objects
restore_objects: function(action, treeItem) {
-
- var i = treeItem || pgBrowser.tree.selected(),
- server_data = null;
-
- while (i) {
- var node_data = pgBrowser.tree.itemData(i);
- if (node_data._type == 'server') {
- server_data = node_data;
- break;
- }
-
- if (pgBrowser.tree.hasParent(i)) {
- i = $(pgBrowser.tree.parent(i));
- } else {
- alertify.alert(
- gettext('Restore Error'),
- gettext('Please select server or child node from tree.')
- );
- break;
- }
- }
-
- if (!server_data) {
- return;
- }
-
- var module = 'paths',
- preference_name = 'pg_bin_dir',
- msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
-
- if ((server_data.type && server_data.type == 'ppas') ||
- server_data.server_type == 'ppas') {
- preference_name = 'ppas_bin_dir';
- msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
- }
-
- var preference = pgBrowser.get_preference(module, preference_name);
-
- if (preference) {
- if (!preference.value) {
- alertify.alert(gettext('Configuration required'), msg);
- return;
- }
- } else {
- alertify.alert(
- gettext('Restore Error'),
- S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value()
- );
- return;
- }
-
- var title = S(gettext('Restore (%s: %s)')),
- tree = pgBrowser.tree,
- item = treeItem || tree.selected(),
- data = item && item.length == 1 && tree.itemData(item),
- node = data && data._type && pgBrowser.Nodes[data._type];
-
- if (!node)
- return;
-
- var treeInfo = node.getTreeNodeHierarchy.apply(node, [item]);
-
- if (treeInfo.database._label.indexOf('=') >= 0) {
- alertify.alert(
- gettext('Restore error'),
- gettext('Restore job creation failed. '+
- 'Databases with = symbols in the name cannot be restored using this utility.')
- );
- return;
- }
-
- title = title.sprintf(node.label, data.label).value();
-
- if (!alertify.pg_restore) {
- // Create Dialog title on the fly with node details
- alertify.dialog('pg_restore', function factory() {
- return {
- main: function(title, item, data, node) {
- this.set('title', title);
- this.setting('pg_node', node);
- this.setting('pg_item', item);
- this.setting('pg_item_data', data);
- },
- build: function() {
- alertify.pgDialogBuild.apply(this);
- },
- setup: function() {
- return {
- buttons: [{
- text: '',
- className: 'btn btn-default pull-left fa fa-lg fa-info',
- attrs: {
- name: 'object_help',
- type: 'button',
- url: 'backup.html',
- label: gettext('Restore'),
- },
- }, {
- text: '',
- key: 112,
- className: 'btn btn-default pull-left fa fa-lg fa-question',
- attrs: {
- name: 'dialog_help',
- type: 'button',
- label: gettext('Restore'),
- url: url_for('help.static', {
- 'filename': 'restore_dialog.html',
- }),
- },
- }, {
- text: gettext('Restore'),
- key: 13,
- className: 'btn btn-primary fa fa-upload pg-alertify-button',
- restore: true,
- 'data-btn-name': 'restore',
- }, {
- text: gettext('Cancel'),
- key: 27,
- className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
- restore: false,
- 'data-btn-name': 'cancel',
- }],
- // Set options for dialog
- options: {
- title: title,
- //disable both padding and overflow control.
- padding: !1,
- overflow: !1,
- model: 0,
- resizable: true,
- maximizable: true,
- pinnable: false,
- closableByDimmer: false,
- modal: false,
- },
- };
- },
- hooks: {
- // triggered when the dialog is closed
- onclose: function() {
- if (this.view) {
- this.view.remove({
- data: true,
- internal: true,
- silent: true,
- });
- }
- },
- },
- settings: {
- pg_node: null,
- pg_item: null,
- pg_item_data: null,
- },
- prepare: function() {
-
- var self = this;
- // Disable Backup button until user provides Filename
- this.__internal.buttons[2].element.disabled = true;
- var $container = $('<div class=\'restore_dialog\'></div>');
- var t = pgBrowser.tree,
- i = t.selected(),
- d = i && i.length == 1 ? t.itemData(i) : undefined,
- node = d && pgBrowser.Nodes[d._type];
-
- if (!d)
- return;
-
- var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
-
- var newModel = new RestoreObjectModel({
- node_data: node,
- }, {
- node_info: treeInfo,
- }),
- fields = Backform.generateViewSchema(
- treeInfo, newModel, 'create', node, treeInfo.server, true
- );
-
- var view = this.view = new Backform.Dialog({
- el: $container,
- model: newModel,
- schema: fields,
- });
-
- $(this.elements.body.childNodes[0]).addClass(
- 'alertify_tools_dialog_properties obj_properties'
- );
-
- view.render();
-
- this.elements.content.appendChild($container.get(0));
-
- view.$el.attr('tabindex', -1);
- // var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
- pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
- var container = view.$el.find('.tab-content:first > .tab-pane.active:first');
- commonUtils.findAndSetFocus(container);
-
- // Listen to model & if filename is provided then enable Backup button
- this.view.model.on('change', function() {
- if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
- this.errorModel.clear();
- self.__internal.buttons[2].element.disabled = false;
- } else {
- self.__internal.buttons[2].element.disabled = true;
- this.errorModel.set('file', gettext('Please provide filename'));
- }
- });
-
- },
- // Callback functions when click on the buttons of the Alertify dialogs
- callback: function(e) {
- // Fetch current server id
- var t = pgBrowser.tree,
- i = this.settings['pg_item'] || t.selected(),
- d = this.settings['pg_item_data'] || (
- i && i.length == 1 ? t.itemData(i) : undefined
- ),
- node = this.settings['pg_node'] || (
- d && pgBrowser.Nodes[d._type]
- );
-
- if (e.button.element.name == 'dialog_help' || e.button.element.name == 'object_help') {
- e.cancel = true;
- pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
- node, i, e.button.element.getAttribute('label'));
- return;
- }
-
- if (e.button['data-btn-name'] === 'restore') {
- if (!d)
- return;
-
- var info = node.getTreeNodeHierarchy.apply(node, [i]),
- m = this.view.model;
- // Set current node info into model
- m.set('database', info.database._label);
- if (!m.get('custom')) {
- switch (d._type) {
- case 'schema':
- m.set('schemas', [d._label]);
- break;
- case 'table':
- m.set('schemas', [info.schema._label]);
- m.set('tables', [d._label]);
- break;
- case 'function':
- m.set('schemas', [info.schema._label]);
- m.set('functions', [d._label]);
- break;
- case 'index':
- m.set('schemas', [info.schema._label]);
- m.set('indexes', [d._label]);
- break;
- case 'trigger':
- m.set('schemas', [info.schema._label]);
- m.set('triggers', [d._label]);
- break;
- case 'trigger_func':
- m.set('schemas', [info.schema._label]);
- m.set('trigger_funcs', [d._label]);
- break;
- }
- } else {
- // TODO::
- // When we will implement the object selection in the
- // import dialog, we will need to select the objects from
- // the tree selection tab.
- }
-
- var self = this,
- baseUrl = url_for('restore.create_job', {
- 'sid': info.server._id,
- }),
- args = this.view.model.toJSON();
-
- $.ajax({
- url: baseUrl,
- method: 'POST',
- data: {
- 'data': JSON.stringify(args),
- },
- success: function(res) {
- if (res.success) {
- alertify.success(
- gettext('Restore job created.'), 5
- );
- pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
- } else {
- console.warn(res);
- }
- },
- error: function(xhr) {
- try {
- var err = $.parseJSON(xhr.responseText);
- alertify.alert(
- gettext('Restore failed.'),
- err.errormsg
- );
- } catch (e) {
- console.warn(e.stack || e);
- }
- },
- });
- }
- },
- };
- });
- }
-
- alertify.pg_restore(title, item, data, node).resizeTo('65%', '60%');
+ let dialog = new restoreDialog.RestoreDialog(pgBrowser, $, alertify, RestoreObjectModel);
+ dialog.draw(action, treeItem);
},
};
return pgBrowser.Restore;
diff --git a/web/pgadmin/tools/restore/static/js/restore_dialog.js b/web/pgadmin/tools/restore/static/js/restore_dialog.js
new file mode 100644
index 00000000..4884d901
--- /dev/null
+++ b/web/pgadmin/tools/restore/static/js/restore_dialog.js
@@ -0,0 +1,57 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from '../../../../static/js/gettext';
+import {sprintf} from 'sprintf-js';
+import Backform from '../../../../static/js/backform.pgadmin';
+import {Dialog} from '../../../../static/js/alertify/dialog';
+
+export class RestoreDialog extends Dialog {
+ constructor(pgBrowser, $, alertify, RestoreModel, backform = Backform) {
+ super('Restore Error',
+ '<div class=\'restore_dialog\'></div>',
+ pgBrowser, $, alertify, RestoreModel, backform);
+ }
+
+ draw(action, aciTreeItem) {
+
+ const serverInformation = this.retrieveAncestorOfTypeServer(aciTreeItem);
+
+ if (!serverInformation) {
+ return;
+ }
+
+ if (!this.hasBinariesConfiguration(serverInformation)) {
+ return;
+ }
+
+ if (!this.canExecuteOnCurrentDatabase(aciTreeItem)) {
+ return;
+ }
+
+ let aciTreeItem1 = aciTreeItem || this.pgBrowser.treeMenu.selected();
+ let item = this.pgBrowser.treeMenu.findNodeByDomElement(aciTreeItem1);
+ const data = item.getData();
+ const node = this.pgBrowser.Nodes[data._type];
+
+ if (!node)
+ return;
+
+ let title = sprintf(gettext('Restore (%s: %s)'), node.label, data.label);
+
+ this.createOrGetDialog(title, 'restore');
+
+ this.alertify.pg_restore(title, aciTreeItem1, data, node).resizeTo('65%', '60%');
+ }
+
+ dialogName() {
+ return 'pg_restore';
+ }
+}
+
diff --git a/web/pgadmin/tools/restore/static/js/restore_dialog_wrapper.js b/web/pgadmin/tools/restore/static/js/restore_dialog_wrapper.js
new file mode 100644
index 00000000..845da7a3
--- /dev/null
+++ b/web/pgadmin/tools/restore/static/js/restore_dialog_wrapper.js
@@ -0,0 +1,255 @@
+import {getTreeNodeHierarchyFromElement} from '../../../../static/js/tree/pgadmin_tree_node';
+import axios from 'axios/index';
+import _ from 'underscore';
+import gettext from '../../../../static/js/gettext';
+import url_for from '../../../../static/js/url_for';
+import {DialogWrapper} from '../../../../static/js/alertify/dialog_wrapper';
+
+export class RestoreDialogWrapper extends DialogWrapper {
+ constructor(dialogContainerSelector, dialogTitle, typeOfDialog,
+ jquery, pgBrowser, alertify, dialogModel, backform) {
+ super(dialogContainerSelector, dialogTitle, jquery,
+ pgBrowser, alertify, dialogModel, backform);
+ }
+
+ main(title, item, data, node) {
+ this.set('title', title);
+ this.setting('pg_node', node);
+ this.setting('pg_item', item);
+ this.setting('pg_item_data', data);
+ }
+
+ setup() {
+ return {
+ buttons: [{
+ text: '',
+ className: 'btn btn-default pull-left fa fa-lg fa-info',
+ attrs: {
+ name: 'object_help',
+ type: 'button',
+ url: 'backup.html',
+ label: gettext('Restore'),
+ },
+ }, {
+ text: '',
+ key: 112,
+ className: 'btn btn-default pull-left fa fa-lg fa-question',
+ attrs: {
+ name: 'dialog_help',
+ type: 'button',
+ label: gettext('Restore'),
+ url: url_for('help.static', {
+ 'filename': 'restore_dialog.html',
+ }),
+ },
+ }, {
+ text: gettext('Restore'),
+ key: 13,
+ className: 'btn btn-primary fa fa-upload pg-alertify-button',
+ restore: true,
+ 'data-btn-name': 'restore',
+ }, {
+ text: gettext('Cancel'),
+ key: 27,
+ className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+ restore: false,
+ 'data-btn-name': 'cancel',
+ }],
+ // Set options for dialog
+ options: {
+ title: this.dialogTitle,
+ //disable both padding and overflow control.
+ padding: !1,
+ overflow: !1,
+ model: 0,
+ resizable: true,
+ maximizable: true,
+ pinnable: false,
+ closableByDimmer: false,
+ modal: false,
+ },
+ };
+ }
+
+ prepare() {
+ this.disableRestoreButton();
+
+ const $container = this.jquery(this.dialogContainerSelector);
+ const selectedTreeNode = this.getSelectedNode();
+ const selectedTreeNodeData = this.getSelectedNodeData(selectedTreeNode);
+ if (!selectedTreeNodeData) {
+ return;
+ }
+
+ const node = this.pgBrowser.Nodes[selectedTreeNodeData._type];
+
+ const treeInfo = getTreeNodeHierarchyFromElement(this.pgBrowser, selectedTreeNode);
+ const dialog = this.createDialog(node, treeInfo, $container);
+ this.addAlertifyClassToRestoreNodeChildNodes();
+ dialog.render();
+
+ this.elements.content.appendChild($container.get(0));
+
+ this.focusOnDialog(dialog);
+ this.setListenersForFilenameChanges();
+ }
+
+ callback(event) {
+ const selectedTreeNode = this.getSelectedNode();
+ const selectedTreeNodeData = this.getSelectedNodeData(selectedTreeNode);
+ const node = selectedTreeNodeData && this.pgBrowser.Nodes[selectedTreeNodeData._type];
+
+ if (this.wasHelpButtonPressed(event)) {
+ event.cancel = true;
+ this.pgBrowser.showHelp(
+ event.button.element.name,
+ event.button.element.getAttribute('url'),
+ node,
+ selectedTreeNode,
+ event.button.element.getAttribute('label')
+ );
+ return;
+ }
+
+ if (this.wasRestoreButtonPressed(event)) {
+
+ if (!selectedTreeNodeData)
+ return;
+
+ const serverIdentifier = this.retrieveServerIdentifier(node, selectedTreeNode);
+
+ const dialogWrapper = this;
+ let urlShortcut = 'restore.create_job';
+
+ const baseUrl = url_for(urlShortcut, {
+ 'sid': serverIdentifier,
+ });
+
+ const treeInfo = getTreeNodeHierarchyFromElement(
+ this.pgBrowser,
+ selectedTreeNode
+ );
+
+ this.setExtraParameters(selectedTreeNode, treeInfo);
+
+ let service = axios.create({});
+ service.post(
+ baseUrl,
+ this.view.model.toJSON()
+ ).then(function () {
+ dialogWrapper.alertify.success(gettext('Restore job created.'), 5);
+ dialogWrapper.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialogWrapper);
+ }).catch(function (error) {
+ try {
+ const err = error.response.data;
+ dialogWrapper.alertify.alert(
+ gettext('Restore job failed.'),
+ err.errormsg
+ );
+ } catch (e) {
+ console.warn(e.stack || e);
+ }
+ });
+ }
+ }
+
+ addAlertifyClassToRestoreNodeChildNodes() {
+ this.jquery(this.elements.body.childNodes[0]).addClass(
+ 'alertify_tools_dialog_properties obj_properties'
+ );
+ }
+
+ getSelectedNode() {
+ const tree = this.pgBrowser.treeMenu;
+ const selectedNode = tree.selected();
+ if (selectedNode) {
+ return tree.findNodeByDomElement(selectedNode);
+ } else {
+ return undefined;
+ }
+ }
+
+ disableRestoreButton() {
+ this.__internal.buttons[2].element.disabled = true;
+ }
+
+ enableRestoreButton() {
+ this.__internal.buttons[2].element.disabled = false;
+ }
+
+ createDialog(node, treeInfo, $container) {
+ const newModel = new this.dialogModel({
+ node_data: node,
+ }, {
+ node_info: treeInfo,
+ });
+ const fields = this.backform.generateViewSchema(
+ treeInfo, newModel, 'create', node, treeInfo.server, true
+ );
+
+ return this.view = new this.backform.Dialog({
+ el: $container,
+ model: newModel,
+ schema: fields,
+ });
+ }
+
+ retrieveServerIdentifier(node, selectedTreeNode) {
+ const treeInfo = getTreeNodeHierarchyFromElement(
+ this.pgBrowser,
+ selectedTreeNode
+ );
+ return treeInfo.server._id;
+ }
+
+ setListenersForFilenameChanges() {
+ const self = this;
+
+ this.view.model.on('change', function () {
+ if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
+ this.errorModel.clear();
+ self.enableRestoreButton();
+ } else {
+ self.disableRestoreButton();
+ this.errorModel.set('file', gettext('Please provide a filename'));
+ }
+ });
+ }
+
+ setExtraParameters(selectedTreeNode, treeInfo) {
+ this.view.model.set('database', treeInfo.database._label);
+ if (!this.view.model.get('custom')) {
+ const nodeData = selectedTreeNode.getData();
+
+ switch (nodeData._type) {
+ case 'schema':
+ this.view.model.set('schemas', [nodeData._label]);
+ break;
+ case 'table':
+ this.view.model.set('schemas', [treeInfo.schema._label]);
+ this.view.model.set('tables', [nodeData._label]);
+ break;
+ case 'function':
+ this.view.model.set('schemas', [treeInfo.schema._label]);
+ this.view.model.set('functions', [nodeData._label]);
+ break;
+ case 'index':
+ this.view.model.set('schemas', [treeInfo.schema._label]);
+ this.view.model.set('indexes', [nodeData._label]);
+ break;
+ case 'trigger':
+ this.view.model.set('schemas', [treeInfo.schema._label]);
+ this.view.model.set('triggers', [nodeData._label]);
+ break;
+ case 'trigger_func':
+ this.view.model.set('schemas', [treeInfo.schema._label]);
+ this.view.model.set('trigger_funcs', [nodeData._label]);
+ break;
+ }
+ }
+ }
+
+ wasRestoreButtonPressed(event) {
+ return event.button['data-btn-name'] === 'restore';
+ }
+}
diff --git a/web/regression/javascript/backup/backup_dialog_spec.js b/web/regression/javascript/backup/backup_dialog_spec.js
new file mode 100644
index 00000000..2655059d
--- /dev/null
+++ b/web/regression/javascript/backup/backup_dialog_spec.js
@@ -0,0 +1,205 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
+import {TreeFake} from '../tree/tree_fake';
+
+const context = describe;
+
+describe('BackupDialog', () => {
+ let backupDialog;
+ let pgBrowser;
+ let jquerySpy;
+ let alertifySpy;
+ let backupModelSpy;
+
+ beforeEach(() => {
+ pgBrowser = {
+ treeMenu: new TreeFake(),
+ Nodes: {
+ server: {
+ hasId: true,
+ label: 'server',
+ getTreeNodeHierarchy: jasmine.createSpy('server.getTreeNodeHierarchy'),
+ },
+ database: {
+ hasId: true,
+ label: 'database',
+ getTreeNodeHierarchy: jasmine.createSpy('db.getTreeNodeHierarchy'),
+ },
+ schema: {
+ hasId: true,
+ label: 'schema',
+ getTreeNodeHierarchy: jasmine.createSpy('db.getTreeNodeHierarchy'),
+ },
+ },
+ };
+ pgBrowser.Nodes.server.hasId = true;
+ pgBrowser.Nodes.database.hasId = true;
+ jquerySpy = jasmine.createSpy('jquerySpy');
+ backupModelSpy = jasmine.createSpy('backupModelSpy');
+
+ const hierarchy = {
+ children: [
+ {
+ id: 'root',
+ children: [
+ {
+ id: 'serverTreeNode',
+ data: {
+ _id: 10,
+ _type: 'server',
+ },
+ children: [
+ {
+ id: 'some_database',
+ data: {
+ _type: 'database',
+ _id: 11,
+ label: 'some_database',
+ _label: 'some_database_label',
+ },
+ }, {
+ id: 'database_with_equal_in_name',
+ data: {
+ _type: 'database',
+ label: 'some_database',
+ _label: '=some_database_label',
+ },
+ },
+ ],
+ },
+ {
+ id: 'ppasServer',
+ data: {
+ _type: 'server',
+ server_type: 'ppas',
+ children: [
+ {id: 'someNodeUnderneathPPASServer'},
+ ],
+ },
+ },
+ ],
+ },
+ ],
+ };
+
+ pgBrowser.treeMenu = TreeFake.build(hierarchy);
+ });
+
+ describe('#draw', () => {
+ beforeEach(() => {
+ alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+ alertifySpy['backup_objects'] = jasmine.createSpy('backup_objects');
+ backupDialog = new BackupDialog(
+ pgBrowser,
+ jquerySpy,
+ alertifySpy,
+ backupModelSpy
+ );
+
+ pgBrowser.get_preference = jasmine.createSpy('get_preferences');
+ });
+
+ context('there are no ancestors of the type server', () => {
+ it('does not create a dialog', () => {
+ pgBrowser.treeMenu.selectNode([{id: 'root'}]);
+ backupDialog.draw(null, null, null);
+ expect(alertifySpy['backup_objects']).not.toHaveBeenCalled();
+ });
+
+ it('display an alert with a Backup Error', () => {
+ backupDialog.draw(null, [{id: 'root'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Backup Error',
+ 'Please select server or child node from the browser tree.'
+ );
+ });
+ });
+
+ context('there is an ancestor of the type server', () => {
+ context('no preference can be found', () => {
+ beforeEach(() => {
+ pgBrowser.get_preference.and.returnValue(undefined);
+ });
+
+ context('server is a ppas server', () => {
+ it('display an alert with "Backup Error"', () => {
+ backupDialog.draw(null, [{id: 'some_database'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Backup Error',
+ 'Failed to load preference pg_bin_dir of module paths'
+ );
+ });
+ });
+
+ context('server is not a ppas server', () => {
+ it('display an alert with "Backup Error"', () => {
+ backupDialog.draw(null, [{id: 'ppasServer'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Backup Error',
+ 'Failed to load preference ppas_bin_dir of module paths'
+ );
+ });
+ });
+ });
+
+ context('preference can be found', () => {
+ context('binary folder is not configured', () => {
+ beforeEach(() => {
+ pgBrowser.get_preference.and.returnValue({});
+ });
+
+ context('server is a ppas server', () => {
+ it('display an alert with "Configuration required"', () => {
+ backupDialog.draw(null, [{id: 'serverTreeNode'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Configuration required',
+ 'Please configure the PostgreSQL Binary Path in the Preferences dialog.'
+ );
+ });
+ });
+
+ context('server is not a ppas server', () => {
+ it('display an alert with "Configuration required"', () => {
+ backupDialog.draw(null, [{id: 'ppasServer'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Configuration required',
+ 'Please configure the EDB Advanced Server Binary Path in the Preferences dialog.'
+ );
+ });
+ });
+ });
+
+ context('binary folder is configured', () => {
+ let backupDialogResizeToSpy;
+ beforeEach(() => {
+ backupDialogResizeToSpy = jasmine.createSpyObj('backupDialogResizeToSpy', ['resizeTo']);
+ alertifySpy['backup_objects'].and
+ .returnValue(backupDialogResizeToSpy);
+ pgBrowser.get_preference.and.returnValue({value: '/some/path'});
+ });
+
+ it('displays the dialog', () => {
+ backupDialog.draw(null, [{id: 'serverTreeNode'}], null);
+ expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true);
+ expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
+ });
+
+ context('database label contain "="', () => {
+ it('should create alert dialog with backup error', () => {
+ backupDialog.draw(null, [{id: 'database_with_equal_in_name'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith('Backup Error',
+ 'Databases with = symbols in the name cannot be backed up or restored using this utility.');
+ });
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/backup/backup_dialog_wrapper_spec.js b/web/regression/javascript/backup/backup_dialog_wrapper_spec.js
new file mode 100644
index 00000000..58705318
--- /dev/null
+++ b/web/regression/javascript/backup/backup_dialog_wrapper_spec.js
@@ -0,0 +1,675 @@
+import {TreeFake} from '../tree/tree_fake';
+import {BackupDialogWrapper} from '../../../pgadmin/tools/backup/static/js/backup_dialog_wrapper';
+import axios from 'axios/index';
+import MockAdapter from 'axios-mock-adapter';
+import {FakeModel} from '../fake_model';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+let context = describe;
+
+describe('BackupDialogWrapper', () => {
+ let jquerySpy;
+ let pgBrowser;
+ let alertifySpy;
+ let dialogModelKlassSpy;
+ let backform;
+ let generatedBackupModel;
+ let backupDialogWrapper;
+ let noDataNode;
+ let serverTreeNode;
+ let databaseTreeNode;
+ let viewSchema;
+ let backupJQueryContainerSpy;
+ let backupNodeChildNodeSpy;
+ let backupNode;
+
+ beforeEach(() => {
+ pgBrowser = {
+ treeMenu: new TreeFake(),
+ Nodes: {
+ server: {
+ hasId: true,
+ getTreeNodeHierarchy: jasmine.createSpy('getTreeNodeHierarchy'),
+ },
+ database: {
+ hasId: true,
+ },
+ },
+ keyboardNavigation: jasmine.createSpyObj('keyboardNavigation', ['getDialogTabNavigator']),
+ };
+ noDataNode = pgBrowser.treeMenu.addNewNode('level1.1', undefined, [{id: 'level1'}]);
+ serverTreeNode = pgBrowser.treeMenu.addNewNode('level2.1', {
+ _type: 'server',
+ _id: 10,
+ label: 'some-tree-label',
+ }, [{id: 'level2.1'}]);
+ databaseTreeNode = new TreeNode('database-tree-node', {
+ _type: 'database',
+ _label: 'some-database-label',
+ }, [{id: 'database-tree-node'}]);
+ pgBrowser.treeMenu.addChild(serverTreeNode, databaseTreeNode);
+
+ jquerySpy = jasmine.createSpy('jquerySpy');
+ backupNode = {
+ __internal: {
+ buttons: [{}, {}, {
+ element: {
+ disabled: false,
+ },
+ }],
+ },
+ elements: {
+ body: {
+ childNodes: [
+ {},
+ ],
+ },
+ content: jasmine.createSpyObj('content', ['appendChild', 'attr']),
+ },
+ };
+
+ backupJQueryContainerSpy = jasmine.createSpyObj('backupJQueryContainer', ['get', 'attr']);
+ backupJQueryContainerSpy.get.and.returnValue(backupJQueryContainerSpy);
+
+ generatedBackupModel = {};
+ dialogModelKlassSpy = jasmine.createSpy('dialogModelKlass');
+ dialogModelKlassSpy.and.returnValue(generatedBackupModel);
+
+ viewSchema = {};
+ backform = jasmine.createSpyObj('backform', ['generateViewSchema', 'Dialog']);
+ backform.generateViewSchema.and.returnValue(viewSchema);
+
+ backupNodeChildNodeSpy = jasmine.createSpyObj('something', ['addClass']);
+ jquerySpy.and.callFake((selector) => {
+ if (selector === '<div class=\'backup_dialog\'></div>') {
+ return backupJQueryContainerSpy;
+ } else if (selector === backupNode.elements.body.childNodes[0]) {
+ return backupNodeChildNodeSpy;
+ }
+ });
+
+ });
+
+ describe('#prepare', () => {
+ beforeEach(() => {
+ alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+ backupDialogWrapper = new BackupDialogWrapper(
+ '<div class=\'backup_dialog\'></div>',
+ 'backupDialogTitle',
+ 'backup',
+ jquerySpy,
+ pgBrowser,
+ alertifySpy,
+ dialogModelKlassSpy,
+ backform
+ );
+ backupDialogWrapper = Object.assign(backupDialogWrapper, backupNode);
+ });
+
+ context('no tree element is selected', () => {
+ it('does not create a backform dialog', () => {
+ backupDialogWrapper.prepare();
+ expect(backform.Dialog).not.toHaveBeenCalled();
+ });
+
+ it('disables the button "submit button" until a filename is selected', () => {
+ backupDialogWrapper.prepare();
+ expect(backupDialogWrapper.__internal.buttons[2].element.disabled).toBe(true);
+ });
+ });
+
+ context('selected tree node has no data', () => {
+ beforeEach(() => {
+ pgBrowser.treeMenu.selectNode(noDataNode.domNode);
+ });
+
+ it('does not create a backform dialog', () => {
+ backupDialogWrapper.prepare();
+ expect(backform.Dialog).not.toHaveBeenCalled();
+ });
+
+ it('disables the button "submit button" until a filename is selected', () => {
+ backupDialogWrapper.prepare();
+ expect(backupDialogWrapper.__internal.buttons[2].element.disabled).toBe(true);
+ });
+ });
+
+ context('tree element is selected', () => {
+ let treeHierarchyInformation;
+ let dialogSpy;
+
+ beforeEach(() => {
+ treeHierarchyInformation = {
+ server: {
+ _type: 'server',
+ _id: 10,
+ priority: 0,
+ label: 'some-tree-label',
+ },
+ };
+ pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+ pgBrowser.Nodes['server'].getTreeNodeHierarchy.and
+ .returnValue(treeHierarchyInformation);
+ dialogSpy = jasmine.createSpyObj('newView', ['render']);
+ dialogSpy.$el = jasmine.createSpyObj('$el', ['find', 'attr']);
+ dialogSpy.model = jasmine.createSpyObj('model', ['on']);
+ dialogSpy.$el.find.and.returnValue([]);
+
+ backform.Dialog.and.returnValue(dialogSpy);
+ });
+
+ it('creates a backform dialog and displays it', () => {
+ backupDialogWrapper.prepare();
+ expect(backform.Dialog).toHaveBeenCalledWith({
+ el: backupJQueryContainerSpy,
+ model: generatedBackupModel,
+ schema: viewSchema,
+ });
+
+ expect(dialogSpy.render).toHaveBeenCalled();
+ });
+
+
+ it('add alertify classes to restore node childnode', () => {
+ backupDialogWrapper.prepare();
+ expect(backupNodeChildNodeSpy.addClass)
+ .toHaveBeenCalledWith('alertify_tools_dialog_properties obj_properties');
+ });
+
+ it('disables the button submit button until a filename is selected', () => {
+ backupDialogWrapper.prepare();
+ expect(backupNode.__internal.buttons[2].element.disabled).toBe(true);
+ });
+
+ it('generates a new backup model', () => {
+ backupDialogWrapper.prepare();
+ expect(dialogModelKlassSpy).toHaveBeenCalledWith(
+ {type: 'backup'},
+ {node_info: treeHierarchyInformation}
+ );
+ });
+
+ it('add the new dialog to the backup node HTML', () => {
+ backupDialogWrapper.prepare();
+ expect(backupNode.elements.content.appendChild).toHaveBeenCalledWith(backupJQueryContainerSpy);
+ });
+ });
+ });
+
+ describe('onButtonClicked', () => {
+ let networkMock;
+ beforeEach(() => {
+ networkMock = new MockAdapter(axios);
+ backupDialogWrapper = new BackupDialogWrapper(
+ '<div class=\'backup_dialog\'></div>',
+ 'backupDialogTitle',
+ 'backup',
+ jquerySpy,
+ pgBrowser,
+ alertifySpy,
+ dialogModelKlassSpy,
+ backform
+ );
+
+ backupDialogWrapper = Object.assign(backupDialogWrapper, backupNode);
+ });
+
+ afterEach(() => {
+ networkMock.restore();
+ });
+
+ context('dialog help button was pressed', () => {
+ let networkCalled;
+ beforeEach(() => {
+ networkCalled = false;
+ networkMock.onAny(/.*/).reply(() => {
+ networkCalled = true;
+ return [200, {}];
+ });
+ pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+ pgBrowser.showHelp = jasmine.createSpy('showHelp');
+
+ const event = {
+ button: {
+ element: {
+ name: 'dialog_help',
+ getAttribute: (attributeName) => {
+ if (attributeName === 'url') {
+ return 'http://someurl';
+ } else if (attributeName === 'label') {
+ return 'some label';
+ }
+ },
+ },
+ },
+ };
+ backupDialogWrapper.callback(event);
+ });
+
+ it('displays help for dialog', () => {
+ expect(pgBrowser.showHelp).toHaveBeenCalledWith(
+ 'dialog_help',
+ 'http://someurl',
+ pgBrowser.Nodes['server'],
+ serverTreeNode,
+ 'some label'
+ );
+ });
+
+ it('does not start the backup', () => {
+ expect(networkCalled).toBe(false);
+ });
+ });
+
+ context('object help button was pressed', () => {
+ let networkCalled;
+ beforeEach(() => {
+ networkCalled = false;
+ networkMock.onAny(/.*/).reply(() => {
+ networkCalled = true;
+ return [200, {}];
+ });
+ pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+ pgBrowser.showHelp = jasmine.createSpy('showHelp');
+
+ const event = {
+ button: {
+ element: {
+ name: 'object_help',
+ getAttribute: (attributeName) => {
+ if (attributeName === 'url') {
+ return 'http://someurl';
+ } else if (attributeName === 'label') {
+ return 'some label';
+ }
+ },
+ },
+ },
+ };
+ backupDialogWrapper.callback(event);
+ });
+
+ it('displays help for dialog', () => {
+ expect(pgBrowser.showHelp).toHaveBeenCalledWith(
+ 'object_help',
+ 'http://someurl',
+ pgBrowser.Nodes['server'],
+ serverTreeNode,
+ 'some label'
+ );
+ });
+
+ it('does not start the backup', () => {
+ expect(networkCalled).toBe(false);
+ });
+ });
+
+ context('backup button was pressed', () => {
+ context('no tree node is selected', () => {
+ it('does not start the backup', () => {
+ let networkCalled = false;
+ networkMock.onAny(/.*/).reply(() => {
+ networkCalled = true;
+ return [200, {}];
+ });
+
+ let event = {
+ button: {
+ 'data-btn-name': 'backup',
+ element: {
+ getAttribute: () => {
+ return 'http://someurl';
+ },
+ },
+ },
+ };
+
+ backupDialogWrapper.callback(event);
+ expect(networkCalled).toBe(false);
+ });
+ });
+
+ context('tree node has no data', () => {
+ it('does not start the backup', () => {
+ pgBrowser.treeMenu.selectNode(noDataNode.domNode);
+
+ let networkCalled = false;
+ networkMock.onAny(/.*/).reply(() => {
+ networkCalled = true;
+ return [200, {}];
+ });
+
+ let event = {
+ button: {
+ 'data-btn-name': 'backup',
+ element: {
+ getAttribute: () => {
+ return 'http://someurl';
+ },
+ },
+ },
+ };
+
+ backupDialogWrapper.callback(event);
+ expect(networkCalled).toBe(false);
+ });
+ });
+
+ context('tree node has data', () => {
+ context('when dialog type is global', () => {
+ let event;
+ beforeEach(() => {
+ pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+
+ backupDialogWrapper.view = {
+ model: new FakeModel(),
+ };
+
+ event = {
+ button: {
+ 'data-btn-name': 'backup',
+ element: {
+ getAttribute: () => {
+ return 'http://someurl';
+ },
+ },
+ },
+ };
+ });
+
+ context('when the backup job is created successfully', () => {
+ let dataSentToServer;
+ beforeEach(() => {
+ pgBrowser.Events = jasmine.createSpyObj('Events', ['trigger']);
+ alertifySpy.success = jasmine.createSpy('success');
+
+ networkMock.onPost('/backup/job/10').reply((request) => {
+ dataSentToServer = request.data;
+ return [200, {}];
+ });
+ });
+
+ it('creates a success alert box', (done) => {
+ backupDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(alertifySpy.success).toHaveBeenCalledWith(
+ 'Backup job created.',
+ 5
+ );
+ done();
+ }, 0);
+ });
+
+ it('trigger an event to background process', (done) => {
+ backupDialogWrapper.callback(event);
+
+ setTimeout(() => {
+ expect(pgBrowser.Events.trigger).toHaveBeenCalledWith(
+ 'pgadmin-bgprocess:created',
+ backupDialogWrapper
+ );
+ done();
+ }, 0);
+ });
+
+ it('send the correct paramenters to the backend', (done) => {
+ backupDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(JSON.parse(dataSentToServer)).toEqual(
+ {}
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ context('when creating backup job fails', () => {
+ it('creates an alert box', (done) => {
+ alertifySpy.alert = jasmine.createSpy('alert');
+ networkMock.onPost('/backup/job/10').reply(() => {
+ return [400, {
+ errormsg: 'some-error-message',
+ }];
+ });
+
+ backupDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Backup job failed.',
+ 'some-error-message'
+ );
+ done();
+ }, 0);
+
+ });
+ });
+ });
+
+ context('when dialog type is object', () => {
+ let event;
+ beforeEach(() => {
+ backupDialogWrapper = new BackupDialogWrapper(
+ '<div class=\'backup_dialog\'></div>',
+ 'backupDialogTitle',
+ 'backup_objects',
+ jquerySpy,
+ pgBrowser,
+ alertifySpy,
+ dialogModelKlassSpy,
+ backform
+ );
+
+ pgBrowser.treeMenu.selectNode(databaseTreeNode.domNode);
+
+ backupDialogWrapper.view = {
+ model: new FakeModel(),
+ };
+
+ event = {
+ button: {
+ 'data-btn-name': 'backup',
+ element: {
+ getAttribute: () => {
+ return 'http://someurl';
+ },
+ },
+ },
+ };
+ });
+
+ context('when the backup job is created successfully', () => {
+ let dataSentToServer;
+ beforeEach(() => {
+ pgBrowser.Events = jasmine.createSpyObj('Events', ['trigger']);
+ alertifySpy.success = jasmine.createSpy('success');
+
+ networkMock.onPost('/backup/job/10/object').reply((request) => {
+ dataSentToServer = request.data;
+ return [200, {}];
+ });
+ });
+
+ it('creates a success alert box', (done) => {
+ backupDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(alertifySpy.success).toHaveBeenCalledWith(
+ 'Backup job created.',
+ 5
+ );
+ done();
+ }, 0);
+ });
+
+ it('trigger an event to background process', (done) => {
+ backupDialogWrapper.callback(event);
+
+ setTimeout(() => {
+ expect(pgBrowser.Events.trigger).toHaveBeenCalledWith(
+ 'pgadmin-bgprocess:created',
+ backupDialogWrapper
+ );
+ done();
+ }, 0);
+ });
+
+ it('send the correct parameters to the backend', (done) => {
+ backupDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(JSON.parse(dataSentToServer)).toEqual(
+ {database: 'some-database-label'}
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ context('when creating backup job fails', () => {
+ it('creates an alert box', (done) => {
+ alertifySpy.alert = jasmine.createSpy('alert');
+ networkMock.onPost('/backup/job/10/object').reply(() => {
+ return [400, {
+ errormsg: 'some-error-message',
+ }];
+ });
+
+ backupDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Backup job failed.',
+ 'some-error-message'
+ );
+ done();
+ }, 0);
+ });
+ });
+ });
+ });
+ });
+ });
+
+ describe('#setExtraParameters', () => {
+ let selectedTreeNode;
+ let treeInfo;
+ let model;
+
+ context('when dialog type is global', () => {
+ beforeEach(() => {
+ backupDialogWrapper = new BackupDialogWrapper(
+ '<div class=\'backup_dialog\'></div>',
+ 'backupDialogTitle',
+ 'backup',
+ jquerySpy,
+ pgBrowser,
+ alertifySpy,
+ dialogModelKlassSpy,
+ backform
+ );
+
+ treeInfo = {};
+ model = new FakeModel();
+ backupDialogWrapper.view = {
+ model: model,
+ };
+ });
+
+
+ it('sets nothing on the view model', () => {
+ backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+ expect(model.toJSON()).toEqual({});
+ });
+ });
+
+ context('when dialog type is object', () => {
+ beforeEach(() => {
+ backupDialogWrapper = new BackupDialogWrapper(
+ '<div class=\'backup_dialog\'></div>',
+ 'backupDialogTitle',
+ 'backup_objects',
+ jquerySpy,
+ pgBrowser,
+ alertifySpy,
+ dialogModelKlassSpy,
+ backform
+ );
+
+ treeInfo = {
+ database: {
+ _label: 'some-database-label',
+ },
+ schema: {
+ _label: 'some-treeinfo-label',
+ },
+ };
+
+ model = new FakeModel();
+ selectedTreeNode = new TreeNode('some-selected-node',
+ {_type: 'some-type', _label: 'some-selected-label'},
+ []);
+ backupDialogWrapper.view = {
+ model: model,
+ };
+ });
+
+ it('sets the database label on the model', () => {
+ backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ 'database': 'some-database-label',
+ });
+ });
+
+ context('when the selected is a schema type', () => {
+ beforeEach(() => {
+ selectedTreeNode = new TreeNode('some-selected-node',
+ {_type: 'schema', _label: 'some-schema-label'},
+ []);
+ });
+
+ it('sets the schema label on the model', () => {
+ backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ 'database': 'some-database-label',
+ 'schemas': ['some-schema-label'],
+ });
+ });
+ });
+
+ context('when the selected is a table type', () => {
+ beforeEach(() => {
+ selectedTreeNode = new TreeNode('some-selected-node',
+ {_type: 'table', _label: 'some-table-label'},
+ []);
+ });
+
+ it('sets the schema label on the model', () => {
+ backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ 'database': 'some-database-label',
+ 'tables': [['some-treeinfo-label', 'some-table-label']],
+ });
+ });
+ });
+
+ context('when the model has no ratio value', () => {
+ beforeEach(() => {
+ model.set('ratio', '');
+ });
+
+ it('sets clears the ratio value', () => {
+ backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+ expect(model.get('ratio')).toBeUndefined();
+ });
+ });
+
+ context('when the model has a valid ratio value', () => {
+ beforeEach(() => {
+ model.set('ratio', '0.25');
+ });
+
+ it('sets clears the ratio value', () => {
+ backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+ expect(model.get('ratio')).toEqual('0.25');
+ });
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/backup/global_server_backup_dialog_spec.js b/web/regression/javascript/backup/global_server_backup_dialog_spec.js
new file mode 100644
index 00000000..86df672e
--- /dev/null
+++ b/web/regression/javascript/backup/global_server_backup_dialog_spec.js
@@ -0,0 +1,168 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
+import {TreeFake} from '../tree/tree_fake';
+
+const context = describe;
+
+describe('GlobalServerBackupDialog', () => {
+ let backupDialog;
+ let pgBrowser;
+ let jquerySpy;
+ let alertifySpy;
+ let backupModelSpy;
+
+
+ let rootNode;
+ let serverTreeNode;
+ let ppasServerTreeNode;
+
+ beforeEach(() => {
+ pgBrowser = {
+ treeMenu: new TreeFake(),
+ Nodes: {
+ server: jasmine.createSpyObj('Node[server]', ['getTreeNodeHierarchy']),
+ },
+ };
+ pgBrowser.Nodes.server.hasId = true;
+ jquerySpy = jasmine.createSpy('jquerySpy');
+ backupModelSpy = jasmine.createSpy('backupModelSpy');
+
+ rootNode = pgBrowser.treeMenu.addNewNode('level1', {}, undefined, []);
+ serverTreeNode = pgBrowser.treeMenu.addNewNode('level1.1', {
+ _type: 'server',
+ _id: 10,
+ }, undefined, ['level1']);
+ ppasServerTreeNode = pgBrowser.treeMenu.addNewNode('level1.2', {
+ _type: 'server',
+ server_type: 'ppas',
+ }, undefined, ['level1']);
+ pgBrowser.treeMenu.addNewNode('level3', {}, undefined, ['level1', 'level1.2']);
+ pgBrowser.treeMenu.addNewNode('level3.1', undefined, undefined, ['level1', 'level1.2', 'level3']);
+ });
+
+ describe('#draw', () => {
+ beforeEach(() => {
+ alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+ alertifySpy['BackupDialog_globals'] = jasmine.createSpy('BackupDialog_globals');
+ alertifySpy['BackupDialog_server'] = jasmine.createSpy('BackupDialog_server');
+ backupDialog = new BackupDialog(
+ pgBrowser,
+ jquerySpy,
+ alertifySpy,
+ backupModelSpy
+ );
+
+ pgBrowser.get_preference = jasmine.createSpy('get_preferences');
+ });
+
+ context('there are no ancestors of the type server', () => {
+ it('does not create a dialog', () => {
+ pgBrowser.treeMenu.selectNode([{id: 'level1'}]);
+ backupDialog.draw(null, null, null);
+ expect(alertifySpy['BackupDialog_globals']).not.toHaveBeenCalled();
+ expect(alertifySpy['BackupDialog_server']).not.toHaveBeenCalled();
+ });
+
+ it('display an alert with a Backup Error', () => {
+ backupDialog.draw(null, [rootNode], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Backup Error',
+ 'Please select server or child node from the browser tree.'
+ );
+ });
+ });
+
+ context('there is an ancestor of the type server', () => {
+ context('no preference can be found', () => {
+ beforeEach(() => {
+ pgBrowser.get_preference.and.returnValue(undefined);
+ });
+
+ context('server is a ppas server', () => {
+ it('display an alert with "Backup Error"', () => {
+ backupDialog.draw(null, [serverTreeNode], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Backup Error',
+ 'Failed to load preference pg_bin_dir of module paths'
+ );
+ });
+ });
+
+ context('server is not a ppas server', () => {
+ it('display an alert with "Backup Error"', () => {
+ backupDialog.draw(null, [ppasServerTreeNode], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Backup Error',
+ 'Failed to load preference ppas_bin_dir of module paths'
+ );
+ });
+ });
+ });
+
+ context('preference can be found', () => {
+ context('binary folder is not configured', () => {
+ beforeEach(() => {
+ pgBrowser.get_preference.and.returnValue({});
+ });
+
+ context('server is a ppas server', () => {
+ it('display an alert with "Configuration required"', () => {
+ backupDialog.draw(null, [serverTreeNode], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Configuration required',
+ 'Please configure the PostgreSQL Binary Path in the Preferences dialog.'
+ );
+ });
+ });
+
+ context('server is not a ppas server', () => {
+ it('display an alert with "Configuration required"', () => {
+ backupDialog.draw(null, [ppasServerTreeNode], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Configuration required',
+ 'Please configure the EDB Advanced Server Binary Path in the Preferences dialog.'
+ );
+ });
+ });
+ });
+
+ context('binary folder is configured', () => {
+ let globalResizeToSpy;
+ let serverResizeToSpy;
+ beforeEach(() => {
+ globalResizeToSpy = jasmine.createSpyObj('globals', ['resizeTo']);
+ alertifySpy['BackupDialog_globals'].and
+ .returnValue(globalResizeToSpy);
+ serverResizeToSpy = jasmine.createSpyObj('server', ['resizeTo']);
+ alertifySpy['BackupDialog_server'].and
+ .returnValue(serverResizeToSpy);
+ pgBrowser.get_preference.and.returnValue({value: '/some/path'});
+ });
+
+ context('dialog for global backup', () => {
+ it('displays the dialog', () => {
+ backupDialog.draw(null, [serverTreeNode], {globals: true});
+ expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true);
+ expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
+ });
+ });
+
+ context('dialog for server backup', () => {
+ it('displays the dialog', () => {
+ backupDialog.draw(null, [serverTreeNode], {server: true});
+ expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true);
+ expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
+ });
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/backup/menu_utils_spec.js b/web/regression/javascript/backup/menu_utils_spec.js
new file mode 100644
index 00000000..9435d699
--- /dev/null
+++ b/web/regression/javascript/backup/menu_utils_spec.js
@@ -0,0 +1,55 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+
+import {menuEnabledServer} from '../../../pgadmin/tools/backup/static/js/menu_utils';
+
+const context = describe;
+
+describe('backup.menuUtils', () => {
+ describe('#menuEnabledServer', () => {
+ context('provided node data is undefined', () => {
+ it('returns false', () => {
+ expect(menuEnabledServer(undefined)).toBe(false);
+ });
+ });
+
+ context('provided node data is null', () => {
+ it('returns false', () => {
+ expect(menuEnabledServer(null)).toBe(false);
+ });
+ });
+
+ context('current node type is not of the type server', () => {
+ it('returns false', () => {
+ expect(menuEnabledServer({_type: 'schema'})).toBe(false);
+ });
+ });
+
+ context('current node type is of the type server', () => {
+ context('is connected', () => {
+ it('returns true', () => {
+ expect(menuEnabledServer({
+ _type: 'server',
+ connected: true,
+ })).toBe(true);
+ });
+ });
+ context('is not connected', () => {
+ it('returns false', () => {
+ expect(menuEnabledServer({
+ _type: 'server',
+ connected: false,
+ })).toBe(false);
+ });
+ });
+ });
+ });
+});
+
diff --git a/web/regression/javascript/common_keyboard_shortcuts_spec.js b/web/regression/javascript/common_keyboard_shortcuts_spec.js
index 9ea31efd..e27929bf 100644
--- a/web/regression/javascript/common_keyboard_shortcuts_spec.js
+++ b/web/regression/javascript/common_keyboard_shortcuts_spec.js
@@ -11,10 +11,6 @@ import keyboardShortcuts from 'sources/keyboard_shortcuts';
describe('the keyboard shortcuts', () => {
const F1_KEY = 112;
- // const EDIT_KEY = 71; // Key: G -> Grid values
- // const LEFT_ARROW_KEY = 37;
- // const RIGHT_ARROW_KEY = 39;
- // const MOVE_NEXT = 'right';
let debuggerElementSpy, event, debuggerUserShortcutSpy;
debuggerUserShortcutSpy = jasmine.createSpyObj(
diff --git a/web/regression/javascript/datagrid/get_panel_title_spec.js b/web/regression/javascript/datagrid/get_panel_title_spec.js
new file mode 100644
index 00000000..8a344a84
--- /dev/null
+++ b/web/regression/javascript/datagrid/get_panel_title_spec.js
@@ -0,0 +1,82 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {getPanelTitle} from '../../../pgadmin/tools/datagrid/static/js/get_panel_title';
+import {TreeFake} from '../tree/tree_fake';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+const context = describe;
+
+describe('#getPanelTitle', () => {
+ let pgBrowser;
+ let tree;
+ beforeEach(() => {
+ tree = new TreeFake();
+ pgBrowser = {
+ treeMenu: tree,
+ Nodes: {
+ server: {
+ hasId: true,
+ _type: 'server',
+ },
+ database: {
+ hasId: true,
+ _type: 'database',
+ },
+ },
+ };
+ });
+
+ context('selected node does not belong to a server', () => {
+ it('returns undefined', () => {
+ const root = tree.addNewNode('level1', {_type: 'server_groups'});
+ tree.addChild(root, new TreeNode('level1.1', {_type: 'other'}));
+ tree.selectNode([{id: 'level1'}]);
+ expect(getPanelTitle(pgBrowser)).toBeUndefined();
+ });
+ });
+
+ context('selected node belong to a server', () => {
+ context('selected node does not belong to a database', () => {
+ it('returns the server label and the username', () => {
+ tree.addNewNode('level1', {
+ _type: 'server',
+ db: 'other db label',
+ user: {name: 'some user name'},
+ label: 'server label',
+ }, []);
+
+ tree.selectNode([{id: 'level1'}]);
+ expect(getPanelTitle(pgBrowser))
+ .toBe('other db label on some user name@server label');
+ });
+ });
+
+ context('selected node belongs to a database', () => {
+ it('returns the database label and the username', () => {
+ const root = tree.addNewNode('level1', {
+ _type: 'server',
+ db: 'other db label',
+ user: {name: 'some user name'},
+ label: 'server label',
+ });
+ const level1 = new TreeNode('level1.1', {
+ _type: 'database',
+ label: 'db label',
+ });
+ tree.addChild(root, level1);
+ tree.addChild(level1,
+ new TreeNode('level1.1.1', {_type: 'table'}));
+ tree.selectNode([{id: 'level1.1.1'}]);
+ expect(getPanelTitle(pgBrowser))
+ .toBe('db label on some user name@server label');
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/datagrid/show_data_spec.js b/web/regression/javascript/datagrid/show_data_spec.js
new file mode 100644
index 00000000..80d25eb3
--- /dev/null
+++ b/web/regression/javascript/datagrid/show_data_spec.js
@@ -0,0 +1,171 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {showDataGrid} from '../../../pgadmin/tools/datagrid/static/js/show_data';
+import {TreeFake} from '../tree/tree_fake';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+const context = describe;
+
+describe('#show_data', () => {
+ let datagrid;
+ let pgBrowser;
+ let alertify;
+ beforeEach(() => {
+ alertify = jasmine.createSpyObj('alertify', ['alert']);
+ datagrid = {
+ create_transaction: jasmine.createSpy('create_transaction'),
+ };
+ pgBrowser = {
+ treeMenu: new TreeFake(),
+ Nodes: {
+ server_group: {
+ _type: 'server_group',
+ hasId: true,
+ },
+ server: {
+ _type: 'server',
+ hasId: true,
+ },
+ database: {
+ _type: 'database',
+ hasId: true,
+ },
+ schema: {
+ _type: 'schema',
+ hasId: true,
+ },
+ view: {
+ _type: 'view',
+ hasId: true,
+ },
+ catalog: {
+ _type: 'catalog',
+ hasId: true,
+ },
+ },
+ };
+ const parent = pgBrowser.treeMenu.addNewNode('parent', {_type: 'parent'}, []);
+ const serverGroup1 = new TreeNode('server_group1', {
+ _type: 'server_group',
+ _id: 1,
+ });
+ pgBrowser.treeMenu.addChild(parent, serverGroup1);
+
+ const server1 = new TreeNode('server1', {
+ _type: 'server',
+ label: 'server1',
+ server_type: 'pg',
+ _id: 2,
+ }, ['parent', 'server_group1']);
+ pgBrowser.treeMenu.addChild(serverGroup1, server1);
+
+ const database1 = new TreeNode('database1', {
+ _type: 'database',
+ label: 'database1',
+ _id: 3,
+ }, ['parent', 'server_group1', 'server1']);
+ pgBrowser.treeMenu.addChild(server1, database1);
+
+ const schema1 = new TreeNode('schema1', {
+ _type: 'schema',
+ label: 'schema1',
+ _id: 4,
+ });
+ pgBrowser.treeMenu.addChild(database1, schema1);
+
+ const view1 = new TreeNode('view1', {
+ _type: 'view',
+ label: 'view1',
+ _id: 5,
+ }, ['parent', 'server_group1', 'server1', 'database1']);
+ pgBrowser.treeMenu.addChild(database1, view1);
+
+ const catalog1 = new TreeNode('catalog1', {
+ _type: 'catalog',
+ label: 'catalog1',
+ _id: 6,
+ }, ['parent', 'server_group1', 'server1', 'database1']);
+ pgBrowser.treeMenu.addChild(database1, catalog1);
+ });
+
+ context('cannot find the tree node', () => {
+ it('does not create a transaction', () => {
+ showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}]);
+ expect(datagrid.create_transaction).not.toHaveBeenCalled();
+ });
+
+ it('display alert', () => {
+ showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}]);
+ expect(alertify.alert).toHaveBeenCalledWith(
+ 'Data Grid Error',
+ 'No object selected.'
+ );
+ });
+ });
+
+ context('current node is not underneath a server', () => {
+ it('does not create a transaction', () => {
+ showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'parent'}]);
+ expect(datagrid.create_transaction).not.toHaveBeenCalled();
+ });
+ });
+
+ context('current node is not underneath a schema or view or catalog', () => {
+ it('does not create a transaction', () => {
+ showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'database1'}]);
+ expect(datagrid.create_transaction).not.toHaveBeenCalled();
+ });
+ });
+
+ context('current node is underneath a schema', () => {
+ it('does not create a transaction', () => {
+ showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'schema1'}]);
+ expect(datagrid.create_transaction).toHaveBeenCalledWith(
+ '/initialize/datagrid/11/schema/1/2/3/4',
+ null,
+ 'false',
+ 'pg',
+ '',
+ 'server1 - database1 - schema1.schema1',
+ ''
+ );
+ });
+ });
+
+ context('current node is underneath a view', () => {
+ it('does not create a transaction', () => {
+ showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'view1'}]);
+ expect(datagrid.create_transaction).toHaveBeenCalledWith(
+ '/initialize/datagrid/11/view/1/2/3/5',
+ null,
+ 'false',
+ 'pg',
+ '',
+ 'server1 - database1 - view1.view1',
+ ''
+ );
+ });
+ });
+
+ context('current node is underneath a catalog', () => {
+ it('does not create a transaction', () => {
+ showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'catalog1'}]);
+ expect(datagrid.create_transaction).toHaveBeenCalledWith(
+ '/initialize/datagrid/11/catalog/1/2/3/6',
+ null,
+ 'false',
+ 'pg',
+ '',
+ 'server1 - database1 - catalog1.catalog1',
+ ''
+ );
+ });
+ });
+});
diff --git a/web/regression/javascript/datagrid/show_query_tool_spec.js b/web/regression/javascript/datagrid/show_query_tool_spec.js
new file mode 100644
index 00000000..66bd37ce
--- /dev/null
+++ b/web/regression/javascript/datagrid/show_query_tool_spec.js
@@ -0,0 +1,125 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {TreeFake} from '../tree/tree_fake';
+import {showQueryTool} from '../../../pgadmin/tools/datagrid/static/js/show_query_tool';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+const context = describe;
+
+describe('#showQueryTool', () => {
+ let queryTool;
+ let pgBrowser;
+ let alertify;
+ beforeEach(() => {
+ alertify = jasmine.createSpyObj('alertify', ['alert']);
+ queryTool = {
+ create_transaction: jasmine.createSpy('create_transaction'),
+ };
+ pgBrowser = {
+ treeMenu: new TreeFake(),
+ Nodes: {
+ server_group: {
+ _type: 'server_group',
+ hasId: true,
+ },
+ server: {
+ _type: 'server',
+ hasId: true,
+ },
+ database: {
+ _type: 'database',
+ hasId: true,
+ },
+ },
+ };
+ const parent = pgBrowser.treeMenu.addNewNode('parent', {_type: 'parent'});
+ const serverGroup1 = new TreeNode('server_group1', {
+ _type: 'server_group',
+ _id: 1,
+ }, ['parent']);
+ pgBrowser.treeMenu.addChild(parent, serverGroup1);
+
+ const server1 = new TreeNode('server1', {
+ _type: 'server',
+ label: 'server1',
+ server_type: 'pg',
+ _id: 2,
+ });
+ pgBrowser.treeMenu.addChild(serverGroup1, server1);
+
+ const database1 = new TreeNode('database1', {
+ _type: 'database',
+ label: 'database1',
+ _id: 3,
+ });
+ pgBrowser.treeMenu.addChild(server1, database1);
+ });
+
+ context('cannot find the tree node', () => {
+ beforeEach(() => {
+ showQueryTool(queryTool, pgBrowser, alertify, '', [{id: '10'}], 'title');
+ });
+ it('does not create a transaction', () => {
+ expect(queryTool.create_transaction).not.toHaveBeenCalled();
+ });
+
+ it('display alert', () => {
+ expect(alertify.alert).toHaveBeenCalledWith(
+ 'Query Tool Error',
+ 'No object selected.'
+ );
+ });
+ });
+
+ context('current node is not underneath a server', () => {
+ it('does not create a transaction', () => {
+ showQueryTool(queryTool, pgBrowser, alertify, '', [{id: 'parent'}], 'title');
+ expect(queryTool.create_transaction).not.toHaveBeenCalled();
+ });
+
+ it('no alert is displayed', () => {
+ expect(alertify.alert).not.toHaveBeenCalled();
+ });
+ });
+
+ context('current node is underneath a server', () => {
+ context('current node is not underneath a database', () => {
+ it('creates a transaction', () => {
+ showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'server1'}], 'title');
+ expect(queryTool.create_transaction).toHaveBeenCalledWith(
+ '/initialize/query_tool/1/2',
+ null,
+ 'true',
+ 'pg',
+ 'http://someurl',
+ 'title',
+ '',
+ false
+ );
+ });
+ });
+
+ context('current node is underneath a database', () => {
+ it('creates a transaction', () => {
+ showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'database1'}], 'title');
+ expect(queryTool.create_transaction).toHaveBeenCalledWith(
+ '/initialize/query_tool/1/2/3',
+ null,
+ 'true',
+ 'pg',
+ 'http://someurl',
+ 'title',
+ '',
+ false
+ );
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/fake_browser/browser.js b/web/regression/javascript/fake_browser/browser.js
new file mode 100644
index 00000000..195e5c51
--- /dev/null
+++ b/web/regression/javascript/fake_browser/browser.js
@@ -0,0 +1,12 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+let treeMenu = null;
+
+export {treeMenu};
diff --git a/web/regression/javascript/fake_endpoints.js b/web/regression/javascript/fake_endpoints.js
index 54b86a94..c060ba78 100644
--- a/web/regression/javascript/fake_endpoints.js
+++ b/web/regression/javascript/fake_endpoints.js
@@ -12,5 +12,11 @@ define(function () {
'static': '/base/pgadmin/static/<path:filename>',
'sqleditor.poll': '/sqleditor/query_tool/poll/<path:trans_id>',
'sqleditor.query_tool_start': '/sqleditor/query_tool/start/<path:trans_id>',
+ 'backup.create_server_job': '/backup/job/<int:sid>',
+ 'backup.create_object_job': '/backup/job/<int:sid>/object',
+ 'datagrid.initialize_datagrid': '/initialize/datagrid/<int:cmd_type>/<obj_type>/<int:sgid>/<int:sid>/<int:did>/<int:obj_id>',
+ 'datagrid.initialize_query_tool': '/initialize/query_tool/<int:sgid>/<int:sid>',
+ 'datagrid.initialize_query_tool_with_did': '/initialize/query_tool/<int:sgid>/<int:sid>/<int:did>',
+ 'restore.create_job': '/restore/job/<int:sid>',
};
});
diff --git a/web/regression/javascript/fake_model.js b/web/regression/javascript/fake_model.js
new file mode 100644
index 00000000..acfaa532
--- /dev/null
+++ b/web/regression/javascript/fake_model.js
@@ -0,0 +1,21 @@
+export class FakeModel {
+ constructor() {
+ this.values = {};
+ }
+
+ set(key, value) {
+ this.values[key] = value;
+ }
+
+ get(key) {
+ return this.values[key];
+ }
+
+ unset(key) {
+ delete this.values[key];
+ }
+
+ toJSON() {
+ return Object.assign({}, this.values);
+ }
+}
diff --git a/web/regression/javascript/nodes/schema/child_menu_spec.js b/web/regression/javascript/nodes/schema/child_menu_spec.js
new file mode 100644
index 00000000..3d3dc55e
--- /dev/null
+++ b/web/regression/javascript/nodes/schema/child_menu_spec.js
@@ -0,0 +1,253 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+/////////////////////////////////////////////////////////////
+
+import {
+ isTreeItemOfChildOfSchema, childCreateMenuEnabled,
+} from 'pgadmin.schema.dir/schema_child_tree_node';
+
+import * as pgBrowser from 'pgbrowser/browser';
+import {TreeFake} from '../../tree/tree_fake';
+
+describe('#childCreateMenuEnabled', () => {
+ let data;
+ let tree;
+
+ describe(' - when data is not null', () => {
+ beforeEach(() => {
+ data = {};
+ });
+ describe(' and check is false', () => {
+ beforeEach(() => {
+ data = {check: false};
+ });
+ it(', then it returns true', () => {
+ expect(childCreateMenuEnabled({}, {}, data)).toBe(true);
+ });
+ });
+
+ describe(' and check', () => {
+ describe(' is true', () => {
+ beforeEach(() => {
+ data = {check: true};
+ });
+
+ describe(', on schema node', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [{
+ id: 'level2',
+ data: {_type: 'schema'},
+ }],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ pgBrowser.treeMenu = tree;
+ });
+ it(' it is true', () => {
+ expect(childCreateMenuEnabled(
+ {}, [{id: 'level2'}], data
+ )).toBe(true);
+
+ });
+ });
+
+ describe(', on child collection node under schema node ', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [{
+ id: 'level2',
+ data: {_type: 'schema'},
+ children: [{
+ id: 'coll-table',
+ data: {_type: 'coll-table'},
+ }],
+ }],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ pgBrowser.treeMenu = tree;
+ });
+
+ it(' it is true', () => {
+ expect(childCreateMenuEnabled(
+ {}, [{id: 'coll-table'}], data
+ )).toBe(true);
+ });
+ });
+
+ describe(', on one of the child node under schema node ', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [{
+ id: 'level2',
+ data: {_type: 'schema'},
+ children: [{
+ id: 'coll-table',
+ data: {_type: 'coll-table'},
+ children: [{
+ id: 'table/1',
+ data: {_type: 'table'},
+ }],
+ }],
+ }],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ pgBrowser.treeMenu = tree;
+ });
+
+ it(' it is true', () => {
+ expect(childCreateMenuEnabled(
+ {}, [{id: 'table/1'}], data
+ )).toBe(true);
+ });
+ });
+
+ describe(', on catalog node', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [{
+ id: 'level2',
+ data: {_type: 'catalog'},
+ }],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ pgBrowser.treeMenu = tree;
+ });
+ it(' it is false', () => {
+ expect(
+ childCreateMenuEnabled({}, [{id: 'level2'}], data)
+ ).toBe(false);
+ });
+ });
+
+ describe(', on child collection node under catalog node ', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [{
+ id: 'level2',
+ data: {_type: 'catalog'},
+ children: [{
+ id: 'coll-table',
+ data: {_type: 'coll-table'},
+ }],
+ }],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ pgBrowser.treeMenu = tree;
+ });
+
+ it(' it is false', () => {
+ expect(childCreateMenuEnabled(
+ {}, [{id: 'coll-table'}], data
+ )).toBe(false);
+ });
+ });
+
+ describe(', on one of the child node under catalog node ', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [{
+ id: 'level2',
+ data: {_type: 'catalog'},
+ children: [{
+ id: 'coll-table',
+ data: {_type: 'coll-table'},
+ children: [{
+ id: 'table/1',
+ data: {_type: 'table'},
+ }],
+ }],
+ }],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ pgBrowser.treeMenu = tree;
+ });
+
+ it(' it is false', () => {
+ expect(childCreateMenuEnabled(
+ {}, [{id: 'table/1'}], data
+ )).toBe(false);
+ });
+ });
+ });
+ });
+ });
+});
+
+describe('#childDropMenuEnabled', () => {
+ let tree;
+
+ describe(' - the child node under schema node ', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [{
+ id: 'level2',
+ data: {_type: 'schema'},
+ children: [{
+ id: 'coll-table',
+ data: {_type: 'coll-table'},
+ children: [{
+ id: 'table/1',
+ data: {_type: 'table'},
+ }],
+ }],
+ }],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ pgBrowser.treeMenu = tree;
+ });
+
+ it(' it is true', () => {
+ expect(isTreeItemOfChildOfSchema(
+ {}, [{id: 'table/1'}]
+ )).toBe(true);
+ });
+ });
+
+ describe('- the child node under the catalog node ', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [{
+ id: 'level2',
+ data: {_type: 'catalog'},
+ children: [{
+ id: 'coll-table',
+ data: {_type: 'coll-table'},
+ children: [{
+ id: 'table/1',
+ data: {_type: 'table'},
+ }],
+ }],
+ }],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ pgBrowser.treeMenu = tree;
+ });
+
+ it(' it is false', () => {
+ expect(isTreeItemOfChildOfSchema(
+ {}, [{id: 'table/1'}]
+ )).toBe(false);
+ });
+ });
+});
diff --git a/web/regression/javascript/restore/restore_dialog_spec.js b/web/regression/javascript/restore/restore_dialog_spec.js
new file mode 100644
index 00000000..156f56bb
--- /dev/null
+++ b/web/regression/javascript/restore/restore_dialog_spec.js
@@ -0,0 +1,203 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import {TreeFake} from '../tree/tree_fake';
+import {RestoreDialog} from '../../../pgadmin/tools/restore/static/js/restore_dialog';
+
+const context = describe;
+
+describe('RestoreDialog', () => {
+ let restoreDialog;
+ let pgBrowser;
+ let jquerySpy;
+ let alertifySpy;
+ let restoreModelSpy;
+
+ beforeEach(() => {
+ pgBrowser = {
+ treeMenu: new TreeFake(),
+ Nodes: {
+ server: jasmine.createSpyObj('Node[server]', ['getTreeNodeHierarchy']),
+ database: jasmine.createSpyObj('Node[database]', ['getTreeNodeHierarchy']),
+ },
+ };
+ pgBrowser.Nodes.server.hasId = true;
+ pgBrowser.Nodes.database.hasId = true;
+ jquerySpy = jasmine.createSpy('jquerySpy');
+ restoreModelSpy = jasmine.createSpy('restoreModelSpy');
+
+ const hierarchy = {
+ children: [
+ {
+ id: 'root',
+ children: [
+ {
+ id: 'serverTreeNode',
+ data: {
+ _id: 10,
+ _type: 'server',
+ label: 'some-tree-label',
+ },
+ children: [
+ {
+ id: 'some_database',
+ data: {
+ _type: 'database',
+ _id: 11,
+ label: 'some_database',
+ _label: 'some_database_label',
+ },
+ }, {
+ id: 'database_with_equal_in_name',
+ data: {
+ _type: 'database',
+ label: 'some_database',
+ _label: '=some_database_label',
+ },
+ },
+ ],
+ },
+ {
+ id: 'ppasServer',
+ data: {
+ _type: 'server',
+ server_type: 'ppas',
+ children: [
+ {id: 'someNodeUnderneathPPASServer'},
+ ],
+ },
+ },
+ ],
+ },
+ ],
+ };
+
+ pgBrowser.treeMenu = TreeFake.build(hierarchy);
+ });
+
+ describe('#draw', () => {
+ beforeEach(() => {
+ alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+ alertifySpy['pg_restore'] = jasmine.createSpy('pg_restore');
+ restoreDialog = new RestoreDialog(
+ pgBrowser,
+ jquerySpy,
+ alertifySpy,
+ restoreModelSpy
+ );
+
+ pgBrowser.get_preference = jasmine.createSpy('get_preferences');
+ });
+
+ context('there are no ancestors of the type server', () => {
+ it('does not create a dialog', () => {
+ pgBrowser.treeMenu.selectNode([{id: 'root'}]);
+ restoreDialog.draw(null, null, null);
+ expect(alertifySpy['pg_restore']).not.toHaveBeenCalled();
+ });
+
+ it('display an alert with a Restore Error', () => {
+ restoreDialog.draw(null, [{id: 'root'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Restore Error',
+ 'Please select server or child node from the browser tree.'
+ );
+ });
+ });
+
+ context('there is an ancestor of the type server', () => {
+ context('no preference can be found', () => {
+ beforeEach(() => {
+ pgBrowser.get_preference.and.returnValue(undefined);
+ });
+
+ context('server is a ppas server', () => {
+ it('display an alert with "Restore Error"', () => {
+ restoreDialog.draw(null, [{id: 'serverTreeNode'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Restore Error',
+ 'Failed to load preference pg_bin_dir of module paths'
+ );
+ });
+ });
+
+ context('server is not a ppas server', () => {
+ it('display an alert with "Restore Error"', () => {
+ restoreDialog.draw(null, [{id: 'ppasServer'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Restore Error',
+ 'Failed to load preference ppas_bin_dir of module paths'
+ );
+ });
+ });
+ });
+
+ context('preference can be found', () => {
+ context('binary folder is not configured', () => {
+ beforeEach(() => {
+ pgBrowser.get_preference.and.returnValue({});
+ });
+
+ context('server is a ppas server', () => {
+ it('display an alert with "Configuration required"', () => {
+ restoreDialog.draw(null, [{id: 'serverTreeNode'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Configuration required',
+ 'Please configure the PostgreSQL Binary Path in the Preferences dialog.'
+ );
+ });
+ });
+
+ context('server is not a ppas server', () => {
+ it('display an alert with "Configuration required"', () => {
+ restoreDialog.draw(null, [{id: 'ppasServer'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Configuration required',
+ 'Please configure the EDB Advanced Server Binary Path in the Preferences dialog.'
+ );
+ });
+ });
+ });
+
+ context('binary folder is configured', () => {
+ let spy;
+ beforeEach(() => {
+ spy = jasmine.createSpyObj('globals', ['resizeTo']);
+ alertifySpy['pg_restore'].and
+ .returnValue(spy);
+ pgBrowser.get_preference.and.returnValue({value: '/some/path'});
+ pgBrowser.Nodes.server.label = 'some-server-label';
+ });
+
+ it('displays the dialog', () => {
+ restoreDialog.draw(null, [{id: 'serverTreeNode'}], {server: true});
+ expect(alertifySpy['pg_restore']).toHaveBeenCalledWith(
+ 'Restore (some-server-label: some-tree-label)',
+ [{id: 'serverTreeNode'}],
+ {
+ _id: 10,
+ _type: 'server',
+ label: 'some-tree-label',
+ },
+ pgBrowser.Nodes.server
+ );
+ expect(spy.resizeTo).toHaveBeenCalledWith('65%', '60%');
+ });
+
+ context('database label contain "="', () => {
+ it('should create alert dialog with restore error', () => {
+ restoreDialog.draw(null, [{id: 'database_with_equal_in_name'}], null);
+ expect(alertifySpy.alert).toHaveBeenCalledWith('Restore Error',
+ 'Databases with = symbols in the name cannot be backed up or restored using this utility.');
+ });
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/restore/restore_dialog_wrapper_spec.js b/web/regression/javascript/restore/restore_dialog_wrapper_spec.js
new file mode 100644
index 00000000..c2a31d55
--- /dev/null
+++ b/web/regression/javascript/restore/restore_dialog_wrapper_spec.js
@@ -0,0 +1,593 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import {TreeFake} from '../tree/tree_fake';
+import {RestoreDialogWrapper} from '../../../pgadmin/tools/restore/static/js/restore_dialog_wrapper';
+import MockAdapter from 'axios-mock-adapter';
+import axios from 'axios/index';
+import {FakeModel} from '../fake_model';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+let context = describe;
+
+describe('RestoreDialogWrapper', () => {
+ let jquerySpy;
+ let pgBrowser;
+ let alertifySpy;
+ let dialogModelKlassSpy;
+ let backform;
+ let generatedRestoreModel;
+ let restoreDialogWrapper;
+ let noDataNode;
+ let serverTreeNode;
+ let viewSchema;
+ let restoreJQueryContainerSpy;
+ let restoreNodeChildNodeSpy;
+ let restoreNode;
+
+ beforeEach(() => {
+ pgBrowser = {
+ treeMenu: new TreeFake(),
+ Nodes: {
+ server: {
+ hasId: true,
+ getTreeNodeHierarchy: jasmine.createSpy('getTreeNodeHierarchy'),
+ },
+ },
+ keyboardNavigation: jasmine.createSpyObj('keyboardNavigation', ['getDialogTabNavigator']),
+ };
+
+ noDataNode = pgBrowser.treeMenu.addNewNode('level1.1', undefined, [{id: 'level1'}]);
+ serverTreeNode = pgBrowser.treeMenu.addNewNode('level2.1', {
+ _type: 'server',
+ _id: 10,
+ label: 'some-tree-label',
+ }, [{id: 'level2.1'}]);
+ jquerySpy = jasmine.createSpy('jquerySpy');
+ dialogModelKlassSpy = jasmine.createSpy('dialogModelKlass');
+ generatedRestoreModel = {};
+ viewSchema = {};
+ backform = jasmine.createSpyObj('backform', ['generateViewSchema', 'Dialog']);
+ backform.generateViewSchema.and.returnValue(viewSchema);
+ dialogModelKlassSpy.and.returnValue(generatedRestoreModel);
+ restoreJQueryContainerSpy = jasmine.createSpyObj('restoreJQueryContainer', ['get', 'attr']);
+ restoreJQueryContainerSpy.get.and.returnValue(restoreJQueryContainerSpy);
+
+ restoreNode = {
+ __internal: {
+ buttons: [
+ {}, {},
+ {
+ element: {
+ disabled: false,
+ },
+ },
+ ],
+ },
+ elements: {
+ body: {
+ childNodes: [
+ {},
+ ],
+ },
+ content: jasmine.createSpyObj('content', ['appendChild', 'attr']),
+ },
+ };
+
+
+ restoreNodeChildNodeSpy = jasmine.createSpyObj('something', ['addClass']);
+
+ jquerySpy.and.callFake((selector) => {
+ if (selector === '<div class=\'restore_dialog\'></div>') {
+ return restoreJQueryContainerSpy;
+ } else if (selector === restoreNode.elements.body.childNodes[0]) {
+ return restoreNodeChildNodeSpy;
+ }
+ });
+ });
+
+ describe('#prepare', () => {
+
+ beforeEach(() => {
+ alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+ restoreDialogWrapper = new RestoreDialogWrapper(
+ '<div class=\'restore_dialog\'></div>',
+ 'restoreDialogTitle',
+ 'restore',
+ jquerySpy,
+ pgBrowser,
+ alertifySpy,
+ dialogModelKlassSpy,
+ backform
+ );
+ restoreDialogWrapper = Object.assign(restoreDialogWrapper, restoreNode);
+ });
+ context('no tree element is selected', () => {
+ it('does not create a backform dialog', () => {
+ restoreDialogWrapper.prepare();
+ expect(backform.Dialog).not.toHaveBeenCalled();
+ });
+
+ it('disables the button "submit button" until a filename is selected', () => {
+ restoreDialogWrapper.prepare();
+ expect(restoreDialogWrapper.__internal.buttons[2].element.disabled).toBe(true);
+ });
+ });
+
+ context('selected tree node has no data', () => {
+ beforeEach(() => {
+ pgBrowser.treeMenu.selectNode(noDataNode.domNode);
+ });
+
+ it('does not create a backform dialog', () => {
+ restoreDialogWrapper.prepare();
+ expect(backform.Dialog).not.toHaveBeenCalled();
+ });
+
+ it('disables the button "submit button" until a filename is selected', () => {
+ restoreDialogWrapper.prepare();
+ expect(restoreDialogWrapper.__internal.buttons[2].element.disabled).toBe(true);
+ });
+ });
+
+ context('tree element is selected', () => {
+ let treeHierarchyInformation;
+ let dialogSpy;
+ beforeEach(() => {
+ treeHierarchyInformation = {
+ server: {
+ _type: 'server',
+ _id: 10,
+ priority: 0,
+ label: 'some-tree-label',
+ },
+ };
+ pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+ pgBrowser.Nodes['server'].getTreeNodeHierarchy.and
+ .returnValue(treeHierarchyInformation);
+ dialogSpy = jasmine.createSpyObj('newView', ['render']);
+ dialogSpy.$el = jasmine.createSpyObj('$el', ['find', 'attr']);
+ dialogSpy.model = jasmine.createSpyObj('model', ['on']);
+ dialogSpy.$el.find.and.returnValue([]);
+
+ backform.Dialog.and.returnValue(dialogSpy);
+ });
+
+ it('creates a backform dialog and displays it', () => {
+ restoreDialogWrapper.prepare();
+ expect(backform.Dialog).toHaveBeenCalledWith({
+ el: restoreJQueryContainerSpy,
+ model: generatedRestoreModel,
+ schema: viewSchema,
+ });
+
+ expect(dialogSpy.render).toHaveBeenCalled();
+ });
+
+ it('add alertify classes to restore node childnode', () => {
+ restoreDialogWrapper.prepare();
+ expect(restoreNodeChildNodeSpy.addClass)
+ .toHaveBeenCalledWith('alertify_tools_dialog_properties obj_properties');
+ });
+
+ it('disables the button submit button until a filename is selected', () => {
+ restoreDialogWrapper.prepare();
+ expect(restoreNode.__internal.buttons[2].element.disabled).toBe(true);
+ });
+
+ it('generates a new restore model', () => {
+ restoreDialogWrapper.prepare();
+ expect(dialogModelKlassSpy).toHaveBeenCalledWith(
+ {node_data: pgBrowser.Nodes['server']},
+ {node_info: treeHierarchyInformation}
+ );
+ });
+
+ it('add the new dialog to the restore node HTML', () => {
+ restoreDialogWrapper.prepare();
+ expect(restoreNode.elements.content.appendChild).toHaveBeenCalledWith(restoreJQueryContainerSpy);
+ });
+ });
+ });
+
+ describe('onButtonClicked', () => {
+ let networkMock;
+
+ beforeEach(() => {
+ pgBrowser.showHelp = jasmine.createSpy('showHelp');
+ networkMock = new MockAdapter(axios);
+ alertifySpy = jasmine.createSpyObj('alertify', ['success', 'alert']);
+ restoreDialogWrapper = new RestoreDialogWrapper(
+ '<div class=\'restore_dialog\'></div>',
+ 'restoreDialogTitle',
+ 'restore',
+ jquerySpy,
+ pgBrowser,
+ alertifySpy,
+ dialogModelKlassSpy,
+ backform
+ );
+ restoreDialogWrapper = Object.assign(restoreDialogWrapper, restoreNode);
+
+ });
+
+ afterEach(function () {
+ networkMock.restore();
+ });
+
+ context('dialog help button was pressed', () => {
+ let networkCalled;
+ beforeEach(() => {
+ networkCalled = false;
+ pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+ networkMock.onAny(/.+/).reply(() => {
+ networkCalled = true;
+ return [200, {}];
+ });
+
+ const event = {
+ button: {
+ element: {
+ name: 'dialog_help',
+ getAttribute: (attributeName) => {
+ if (attributeName === 'url') {
+ return 'http://someurl';
+ } else if (attributeName === 'label') {
+ return 'some label';
+ }
+ },
+ },
+ },
+ };
+ restoreDialogWrapper.callback(event);
+ });
+
+ it('displays help for dialog', () => {
+ expect(pgBrowser.showHelp).toHaveBeenCalledWith(
+ 'dialog_help',
+ 'http://someurl',
+ pgBrowser.Nodes['server'],
+ serverTreeNode,
+ 'some label'
+ );
+ });
+
+ it('does not start the restore', () => {
+ expect(networkCalled).toBe(false);
+ });
+ });
+
+ context('object help button was pressed', () => {
+ let networkCalled;
+ beforeEach(() => {
+ networkCalled = false;
+ pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+ networkMock.onAny(/.+/).reply(() => {
+ networkCalled = true;
+ return [200, {}];
+ });
+
+ const event = {
+ button: {
+ element: {
+ name: 'object_help',
+ getAttribute: (attributeName) => {
+ if (attributeName === 'url') {
+ return 'http://someurl';
+ } else if (attributeName === 'label') {
+ return 'some label';
+ }
+ },
+ },
+ },
+ };
+ restoreDialogWrapper.callback(event);
+ });
+
+ it('displays help for dialog', () => {
+ expect(pgBrowser.showHelp).toHaveBeenCalledWith(
+ 'object_help',
+ 'http://someurl',
+ pgBrowser.Nodes['server'],
+ serverTreeNode,
+ 'some label'
+ );
+ });
+
+ it('does not start the restore', () => {
+ expect(networkCalled).toBe(false);
+ });
+ });
+
+ context('restore button was pressed', () => {
+ let networkCalled;
+ let event;
+
+ context('no tree node is selected', () => {
+ beforeEach(() => {
+ networkCalled = false;
+ networkMock.onAny(/.+/).reply(() => {
+ networkCalled = true;
+ return [200, {}];
+ });
+ event = {
+ button: {
+ 'data-btn-name': 'restore',
+ element: {
+ getAttribute: () => {
+ return 'http://someurl';
+ },
+ },
+ },
+ };
+ });
+
+ it('does not start the restore', () => {
+ restoreDialogWrapper.callback(event);
+ expect(networkCalled).toBe(false);
+ });
+ });
+
+ context('tree node selected has no data', () => {
+ beforeEach(() => {
+ networkCalled = false;
+ networkMock.onAny(/.+/).reply(() => {
+ networkCalled = true;
+ return [200, {}];
+ });
+ event = {
+ button: {
+ 'data-btn-name': 'restore',
+ element: {
+ getAttribute: () => {
+ return 'http://someurl';
+ },
+ },
+ },
+ };
+ pgBrowser.treeMenu.selectNode(noDataNode.domNode);
+ });
+
+ it('does not start the restore', () => {
+ restoreDialogWrapper.callback(event);
+ expect(networkCalled).toBe(false);
+ });
+ });
+
+ context('tree node select has data', () => {
+
+ let databaseTreeNode;
+
+ beforeEach(() => {
+ databaseTreeNode = pgBrowser.treeMenu.addNewNode('level3.1', {
+ _type: 'database',
+ _id: 10,
+ _label: 'some-database-label',
+ }, [{id: 'level3.1'}]);
+ pgBrowser.treeMenu.addChild(serverTreeNode, databaseTreeNode);
+ pgBrowser.Nodes.database = {
+ hasId: true,
+ _label: 'some-database-label',
+ };
+ let fakeModel = new FakeModel();
+ fakeModel.set('some-key', 'some-value');
+ restoreDialogWrapper.view = {
+ model: fakeModel,
+ };
+ pgBrowser.treeMenu.selectNode(databaseTreeNode.domNode);
+ pgBrowser.Events = jasmine.createSpyObj('pgBrowserEventsSpy', ['trigger']);
+ event = {
+ button: {
+ 'data-btn-name': 'restore',
+ element: {
+ getAttribute: () => {
+ return 'http://someurl';
+ },
+ },
+ },
+ };
+ });
+ context('restore job created successfully', () => {
+ let dataSentToServer;
+ beforeEach(() => {
+ networkMock.onPost('/restore/job/10').reply((request) => {
+ dataSentToServer = request.data;
+ return [200, {}];
+ });
+ });
+
+ it('create an success alert box', (done) => {
+ restoreDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(alertifySpy.success).toHaveBeenCalledWith(
+ 'Restore job created.',
+ 5
+ );
+ done();
+ }, 0);
+ });
+
+ it('trigger background process', (done) => {
+ restoreDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(pgBrowser.Events.trigger).toHaveBeenCalledWith(
+ 'pgadmin-bgprocess:created',
+ restoreDialogWrapper
+ );
+ done();
+ }, 0);
+ });
+
+ it('send correct data to server', (done) => {
+ restoreDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(JSON.parse(dataSentToServer)).toEqual({
+ 'some-key': 'some-value',
+ 'database': 'some-database-label',
+ });
+ done();
+ }, 0);
+ });
+ });
+
+ context('error creating restore job', () => {
+ beforeEach(() => {
+ networkMock.onPost('/restore/job/10').reply(() => {
+ return [400, {}];
+ });
+ });
+
+ it('creates an alert box', (done) => {
+ restoreDialogWrapper.callback(event);
+ setTimeout(() => {
+ expect(alertifySpy.alert).toHaveBeenCalledWith(
+ 'Restore job failed.',
+ undefined
+ );
+ done();
+ }, 0);
+ });
+ });
+ });
+ });
+ });
+
+ describe('setExtraParameters', () => {
+ let selectedNode;
+ let treeInfo;
+ let model;
+
+ beforeEach(() => {
+ restoreDialogWrapper = new RestoreDialogWrapper(
+ '<div class=\'restore_dialog\'></div>',
+ 'restoreDialogTitle',
+ 'restore',
+ jquerySpy,
+ pgBrowser,
+ alertifySpy,
+ dialogModelKlassSpy,
+ backform
+ );
+
+ model = new FakeModel();
+ restoreDialogWrapper.view = {
+ model: model,
+ };
+ });
+
+ context('when it is a custom model', () => {
+ beforeEach(() => {
+ model.set('custom', true);
+ treeInfo = {
+ 'database': {
+ '_label': 'some-database-label',
+ },
+ };
+ });
+
+ it('only sets the database', () => {
+ restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ 'custom': true,
+ 'database': 'some-database-label',
+ });
+ });
+ });
+
+ context('when it is not a custom model', () => {
+ beforeEach(() => {
+ model.set('custom', false);
+ treeInfo = {
+ 'database': {
+ '_label': 'some-database-label',
+ },
+ 'schema': {
+ '_label': 'some-schema-label',
+ },
+ };
+ });
+
+ context('when selected node is a schema', () => {
+ it('sets schemas on the model', () => {
+ selectedNode = new TreeNode('schema', {_type: 'schema', _label: 'some-schema-label'}, '');
+ restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ custom: false,
+ database: 'some-database-label',
+ schemas: ['some-schema-label'],
+ });
+ });
+ });
+
+ context('when selected node is a table', () => {
+ it('sets schemas and table on the model', () => {
+ selectedNode = new TreeNode('table', {_type: 'table', _label: 'some-table-label'}, '');
+ restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ custom: false,
+ database: 'some-database-label',
+ schemas: ['some-schema-label'],
+ tables: ['some-table-label'],
+ });
+ });
+ });
+
+ context('when selected node is a function', () => {
+ it('sets schemas and function on the model', () => {
+ selectedNode = new TreeNode('function', {_type: 'function', _label: 'some-function-label'}, '');
+ restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ custom: false,
+ database: 'some-database-label',
+ schemas: ['some-schema-label'],
+ functions: ['some-function-label'],
+ });
+ });
+ });
+
+ context('when selected node is an index', () => {
+ it('sets schemas and index on the model', () => {
+ selectedNode = new TreeNode('index', {_type: 'index', _label: 'some-index-label'}, '');
+ restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ custom: false,
+ database: 'some-database-label',
+ schemas: ['some-schema-label'],
+ indexes: ['some-index-label'],
+ });
+ });
+ });
+
+ context('when selected node is a trigger', () => {
+ it('sets schemas and trigger on the model', () => {
+ selectedNode = new TreeNode('trigger', {_type: 'trigger', _label: 'some-trigger-label'}, '');
+ restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ custom: false,
+ database: 'some-database-label',
+ schemas: ['some-schema-label'],
+ triggers: ['some-trigger-label'],
+ });
+ });
+ });
+
+ context('when selected node is a trigger_func', () => {
+ it('sets schemas and trigger_func on the model', () => {
+ selectedNode = new TreeNode('trigger_func', {_type: 'trigger_func', _label: 'some-trigger_func-label'}, '');
+ restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+ expect(model.toJSON()).toEqual({
+ custom: false,
+ database: 'some-database-label',
+ schemas: ['some-schema-label'],
+ trigger_funcs: ['some-trigger_func-label'],
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/sqleditor/filter_dialog_specs.js b/web/regression/javascript/sqleditor/filter_dialog_specs.js
index ed77dff5..cea75e6b 100644
--- a/web/regression/javascript/sqleditor/filter_dialog_specs.js
+++ b/web/regression/javascript/sqleditor/filter_dialog_specs.js
@@ -7,10 +7,8 @@
//
//////////////////////////////////////////////////////////////////////////
import filterDialog from 'sources/sqleditor/filter_dialog';
-// import filterDialogModel from 'sources/sqleditor/filter_dialog_model';
describe('filterDialog', () => {
- jasmine.createSpy('sqlEditorController');
describe('filterDialog', () => {
describe('when using filter dialog', () => {
beforeEach(() => {
diff --git a/web/regression/javascript/table/enable_disable_triggers_spec.js b/web/regression/javascript/table/enable_disable_triggers_spec.js
new file mode 100644
index 00000000..7bdd284e
--- /dev/null
+++ b/web/regression/javascript/table/enable_disable_triggers_spec.js
@@ -0,0 +1,271 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import MockAdapter from 'axios-mock-adapter';
+import axios from 'axios/index';
+import {
+ enableTriggers,
+ disableTriggers,
+} from '../../../pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/enable_disable_triggers';
+import {TreeFake} from '../tree/tree_fake';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+describe('#enableTriggers', () => {
+ let networkMock;
+ let tree;
+ let alertify;
+ let generateUrlSpy;
+ beforeEach(() => {
+ networkMock = new MockAdapter(axios);
+ tree = new TreeFake();
+ const server1 = tree.addNewNode('server1', {_id: 1}, ['<li>server1</li>']);
+ const database1 = tree.addNewNode('database1', {_type: 'database'}, ['<li>database1</li>']);
+ tree.addChild(server1, database1);
+
+ const schema1 = tree.addNewNode('schema1', {_type: 'schema'}, ['<li>schema1</li>']);
+ tree.addChild(database1, schema1);
+
+ const table1 = tree.addNewNode('table1', {_type: 'table'}, ['<li>table1</li>']);
+ tree.addChild(schema1, table1);
+
+ const column1 = tree.addNewNode('column1', {_type: 'column'}, ['<li>column1</li>']);
+ tree.addChild(table1, column1);
+
+ const tableNoData = tree.addNewNode('table-no-data', undefined, ['<li>table-no-data</li>']);
+ tree.addChild(schema1, tableNoData);
+
+ alertify = jasmine.createSpyObj('alertify', ['success', 'error']);
+ generateUrlSpy = jasmine.createSpy('generateUrl');
+ generateUrlSpy.and.returnValue('/some/place');
+ });
+
+ describe('no node is selected', () => {
+ it('does not send the request to the backend', (done) => {
+ networkMock.onAny('.*').reply(200, () => {
+ });
+
+ setTimeout(() => {
+ expect(enableTriggers(tree, alertify, generateUrlSpy, {})).toBe(false);
+ done();
+ }, 0);
+ });
+ });
+
+ describe('a node is selected', () => {
+ describe('node as no data', () => {
+ it('does not send the request to the backend', () => {
+ tree.selectNode([{id: 'table-no-data'}]);
+
+ networkMock.onAny('.*').reply(200, () => {
+ });
+
+ setTimeout(() => {
+ expect(enableTriggers(tree, alertify, generateUrlSpy, {})).toBe(false);
+ }, 0);
+ });
+ });
+
+ describe('node as data', () => {
+ describe('backend responds with success', () => {
+ let networkMockCalledWith;
+ beforeEach(() => {
+ networkMockCalledWith = false;
+ networkMock.onPut(/.*/).reply((configuration) => {
+ networkMockCalledWith = configuration;
+ return [200, {
+ success: 1,
+ info: 'some information',
+ }];
+ });
+ });
+
+ it('displays an alert box with success', (done) => {
+ tree.selectNode([{id: 'table1'}]);
+ enableTriggers(tree, alertify, generateUrlSpy, {});
+ setTimeout(() => {
+ expect(alertify.success).toHaveBeenCalledWith('some information');
+ done();
+ }, 0);
+ });
+
+ it('reloads the node', (done) => {
+ enableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+ setTimeout(() => {
+ expect(tree.selected()).toEqual(['<li>table1</li>']);
+ done();
+ }, 20);
+ });
+
+ it('call backend with the correct parameters', (done) => {
+ enableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+ setTimeout(() => {
+ expect(networkMockCalledWith.data).toEqual(JSON.stringify({enable: 'true'}));
+ done();
+ }, 0);
+ });
+ });
+
+ describe('backend responds with error', () => {
+ beforeEach(() => {
+ networkMock.onPut(/.*/).reply(() => {
+ return [500, {
+ success: 0,
+ errormsg: 'some error message',
+ }];
+ });
+ });
+
+ it('displays an error alert', (done) => {
+ tree.selectNode([{id: 'table1'}]);
+ enableTriggers(tree, alertify, generateUrlSpy, {});
+ setTimeout(() => {
+ expect(alertify.error).toHaveBeenCalledWith('some error message');
+ done();
+ }, 0);
+ });
+
+ it('unload the node', (done) => {
+ enableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+
+ setTimeout(() => {
+ expect(tree.findNodeByDomElement([{id: 'table1'}]).children.length).toBe(0);
+ done();
+ }, 20);
+ });
+ });
+ });
+ });
+});
+
+describe('#disableTriggers', () => {
+ let networkMock;
+ let tree;
+ let alertify;
+ let generateUrlSpy;
+ beforeEach(() => {
+ networkMock = new MockAdapter(axios);
+ tree = new TreeFake();
+ const server1 = tree.addNewNode('server1', {_id: 1}, ['<li>server1</li>']);
+ const database1 = new TreeNode('database1', {_type: 'database'}, ['<li>database1</li>']);
+ tree.addChild(server1, database1);
+
+ const schema1 = new TreeNode('schema1', {_type: 'schema'}, ['<li>schema1</li>']);
+ tree.addChild(database1, schema1);
+
+ const table1 = new TreeNode('table1', {_type: 'table'}, ['<li>table1</li>']);
+ tree.addChild(schema1, table1);
+
+ const column1 = new TreeNode('column1', {_type: 'column'}, ['<li>column1</li>']);
+ tree.addChild(table1, column1);
+
+ const tableNoData = new TreeNode('table-no-data', undefined, ['<li>table-no-data</li>']);
+ tree.addChild(schema1, tableNoData);
+
+ alertify = jasmine.createSpyObj('alertify', ['success', 'error']);
+ generateUrlSpy = jasmine.createSpy('generateUrl');
+ generateUrlSpy.and.returnValue('/some/place');
+ });
+
+ describe('no node is selected', () => {
+ it('does not send the request to the backend', (done) => {
+ networkMock.onAny('.*').reply(200, () => {
+ });
+
+ setTimeout(() => {
+ expect(disableTriggers(tree, alertify, generateUrlSpy, {})).toBe(false);
+ done();
+ }, 0);
+ });
+ });
+
+ describe('a node is selected', () => {
+ describe('node as no data', () => {
+ it('does not send the request to the backend', () => {
+ tree.selectNode([{id: 'table-no-data'}]);
+
+ networkMock.onAny('.*').reply(200, () => {
+ });
+
+ setTimeout(() => {
+ expect(disableTriggers(tree, alertify, generateUrlSpy, {})).toBe(false);
+ }, 0);
+ });
+ });
+
+ describe('node as data', () => {
+ describe('backend responds with success', () => {
+ let networkMockCalledWith;
+ beforeEach(() => {
+ networkMockCalledWith = false;
+ networkMock.onPut(/.*/).reply((configuration) => {
+ networkMockCalledWith = configuration;
+ return [200, {
+ success: 1,
+ info: 'some information',
+ }];
+ });
+ });
+
+ it('displays an alert box with success', (done) => {
+ tree.selectNode([{id: 'table1'}]);
+ disableTriggers(tree, alertify, generateUrlSpy, {});
+ setTimeout(() => {
+ expect(alertify.success).toHaveBeenCalledWith('some information');
+ done();
+ }, 0);
+ });
+
+ it('reloads the node', (done) => {
+ disableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+ setTimeout(() => {
+ expect(tree.selected()).toEqual(['<li>table1</li>']);
+ done();
+ }, 20);
+ });
+
+ it('call backend with the correct parameters', (done) => {
+ disableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+ setTimeout(() => {
+ expect(networkMockCalledWith.data).toEqual(JSON.stringify({enable: 'false'}));
+ done();
+ }, 0);
+ });
+ });
+
+ describe('backend responds with error', () => {
+ beforeEach(() => {
+ networkMock.onPut(/.*/).reply(() => {
+ return [500, {
+ success: 0,
+ errormsg: 'some error message',
+ }];
+ });
+ });
+
+ it('displays an error alert', (done) => {
+ tree.selectNode([{id: 'table1'}]);
+ disableTriggers(tree, alertify, generateUrlSpy, {});
+ setTimeout(() => {
+ expect(alertify.error).toHaveBeenCalledWith('some error message');
+ done();
+ }, 0);
+ });
+
+ it('unload the node', (done) => {
+ disableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+
+ setTimeout(() => {
+ expect(tree.findNodeByDomElement([{id: 'table1'}]).children.length).toBe(0);
+ done();
+ }, 20);
+ });
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/tree/pgadmin_tree_node_spec.js b/web/regression/javascript/tree/pgadmin_tree_node_spec.js
new file mode 100644
index 00000000..479e515c
--- /dev/null
+++ b/web/regression/javascript/tree/pgadmin_tree_node_spec.js
@@ -0,0 +1,353 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import {
+ getTreeNodeHierarchyFromElement,
+ getTreeNodeHierarchyFromIdentifier,
+} from '../../../pgadmin/static/js/tree/pgadmin_tree_node';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+import {TreeFake} from './tree_fake';
+
+const context = describe;
+
+describe('tree#node#getTreeNodeHierarchy', () => {
+ let browser;
+ let newTree;
+ beforeEach(() => {
+ newTree = new TreeFake();
+ browser = {
+ Nodes: {
+ 'special one': {hasId: true},
+ 'child special': {hasId: true},
+ 'other type': {hasId: true},
+ 'table': {hasId: true},
+ 'partition': {hasId: true},
+ 'no id': {hasId: false},
+ },
+ };
+ browser.treeMenu = newTree;
+ });
+
+ context('getTreeNodeHierarchy is called with aciTreeNode object', () => {
+ describe('When the current node is root element', () => {
+ beforeEach(() => {
+ newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'special one',
+ });
+ });
+
+ it('returns a object with the element type passed data and priority == 0', () => {
+ const result = getTreeNodeHierarchyFromIdentifier.bind(browser)([{id: 'root'}]);
+ expect(result).toEqual({
+ 'special one': {
+ 'some key': 'some value',
+ '_type': 'special one',
+ 'priority': 0,
+ },
+ });
+ });
+ });
+
+ describe('When the current node is not of a known type', () => {
+ beforeEach(() => {
+ newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'do not exist',
+ }, []);
+ });
+
+ it('returns a empty object', () => {
+ const result = getTreeNodeHierarchyFromIdentifier.bind(browser)('root');
+ expect(result).toEqual({});
+ });
+ });
+
+ describe('When the current node type has no id', () => {
+ beforeEach(() => {
+ newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'no id',
+ }, []);
+ });
+
+ it('returns a empty object', () => {
+ const result = getTreeNodeHierarchyFromIdentifier.bind(browser)('root');
+ expect(result).toEqual({});
+ });
+ });
+
+ describe('When the current node is at the second level', () => {
+ beforeEach(() => {
+ const root = newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'special one',
+ });
+ const firstChild = new TreeNode('first child', {
+ 'some key': 'some other value',
+ '_type': 'child special',
+ }, ['root']);
+ newTree.addChild(root, firstChild);
+ });
+
+ it('returns a empty object', () => {
+ const result = getTreeNodeHierarchyFromIdentifier.bind(browser)([{id: 'first child'}]);
+ expect(result).toEqual({
+ 'child special': {
+ 'some key': 'some other value',
+ '_type': 'child special',
+ 'priority': 0,
+ },
+ 'special one': {
+ 'some key': 'some value',
+ '_type': 'special one',
+ 'priority': -1,
+ },
+ });
+ });
+ });
+
+ context('When tree as "special type"', () => {
+ context('When "special type" have "other type"', () => {
+ context('When "other type" have "special type"', () => {
+ describe('When retrieving lead node', () => {
+ it('does not override previous node type data', () => {
+ const rootNode = newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'special one',
+ }, []);
+
+ const level1 = new TreeNode('level 1', {
+ 'some key': 'some value',
+ '_type': 'other type',
+ });
+ newTree.addChild(rootNode, level1);
+
+ newTree.addChild(level1, new TreeNode('level 2', {
+ 'some key': 'expected value',
+ '_type': 'special one',
+ 'some other key': 'some other value',
+ }));
+
+ const result = getTreeNodeHierarchyFromIdentifier.bind(browser)([{id: 'level 2'}]);
+ expect(result).toEqual({
+ 'special one': {
+ 'some key': 'expected value',
+ '_type': 'special one',
+ 'some other key': 'some other value',
+ 'priority': 0,
+ },
+ 'other type': {
+ 'some key': 'some value',
+ '_type': 'other type',
+ 'priority': -1,
+ },
+ });
+ });
+ });
+ });
+ });
+ });
+
+ context('When tree has table', () => {
+ context('when table has partition', () => {
+ it('returns table with partition parameters', () => {
+ const root = newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'special one',
+ }, []);
+ const level1 = new TreeNode('level 1', {
+ 'some key': 'some value',
+ '_type': 'table',
+ });
+ newTree.addChild(root, level1);
+ newTree.addChild(level1, new TreeNode('level 2', {
+ 'some key': 'expected value',
+ '_type': 'partition',
+ 'some other key': 'some other value',
+ }));
+
+ const result = getTreeNodeHierarchyFromIdentifier.bind(browser)([{id:'level 2'}]);
+ expect(result).toEqual({
+ 'special one': {
+ 'some key': 'some value',
+ '_type': 'special one',
+ 'priority': -1,
+ },
+ 'table': {
+ 'some key': 'expected value',
+ 'some other key': 'some other value',
+ '_type': 'partition',
+ 'priority': 0,
+ },
+ });
+ });
+ });
+ });
+ });
+
+ context('getTreeNodeHierarchy is called with TreeNode object', () => {
+ let treeNode;
+ describe('When the current node is root element', () => {
+ beforeEach(() => {
+ treeNode = newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'special one',
+ }, []);
+ });
+
+ it('returns a object with the element type passed data and priority == 0', () => {
+ const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+ expect(result).toEqual({
+ 'special one': {
+ 'some key': 'some value',
+ '_type': 'special one',
+ 'priority': 0,
+ },
+ });
+ });
+ });
+
+ describe('When the current node is not of a known type', () => {
+ beforeEach(() => {
+ treeNode = newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'do not exist',
+ }, []);
+ });
+
+ it('returns a empty object', () => {
+ const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+ expect(result).toEqual({});
+ });
+ });
+
+ describe('When the current node type has no id', () => {
+ beforeEach(() => {
+ treeNode = newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'no id',
+ }, []);
+ });
+
+ it('returns a empty object', () => {
+ const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+ expect(result).toEqual({});
+ });
+ });
+
+ describe('When the current node is at the second level', () => {
+ beforeEach(() => {
+ const root = newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'special one',
+ }, []);
+ treeNode = new TreeNode('first child', {
+ 'some key': 'some other value',
+ '_type': 'child special',
+ });
+ newTree.addChild(root, treeNode);
+ });
+
+ it('returns a empty object', () => {
+ const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+ expect(result).toEqual({
+ 'child special': {
+ 'some key': 'some other value',
+ '_type': 'child special',
+ 'priority': 0,
+ },
+ 'special one': {
+ 'some key': 'some value',
+ '_type': 'special one',
+ 'priority': -1,
+ },
+ });
+ });
+ });
+
+ context('When tree as "special type"', () => {
+ context('When "special type" have "other type"', () => {
+ context('When "other type" have "special type"', () => {
+ describe('When retrieving lead node', () => {
+ it('does not override previous node type data', () => {
+ const root = newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'special one',
+ }, []);
+ const level1 = new TreeNode('level 1', {
+ 'some key': 'some value',
+ '_type': 'other type',
+ });
+ newTree.addChild(root, level1);
+ treeNode = new TreeNode('level 2', {
+ 'some key': 'expected value',
+ '_type': 'special one',
+ 'some other key': 'some other value',
+ });
+ newTree.addChild(level1, treeNode);
+
+ const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+ expect(result).toEqual({
+ 'special one': {
+ 'some key': 'expected value',
+ '_type': 'special one',
+ 'some other key': 'some other value',
+ 'priority': 0,
+ },
+ 'other type': {
+ 'some key': 'some value',
+ '_type': 'other type',
+ 'priority': -1,
+ },
+ });
+ });
+ });
+ });
+ });
+ });
+
+ context('When tree has table', () => {
+ context('when table has partition', () => {
+ it('returns table with partition parameters', () => {
+ const root = newTree.addNewNode('root', {
+ 'some key': 'some value',
+ '_type': 'special one',
+ });
+ const level1 = newTree.addNewNode('level 1', {
+ 'some key': 'some value',
+ '_type': 'table',
+ });
+ newTree.addChild(root, level1);
+ treeNode = new TreeNode('level 2', {
+ 'some key': 'expected value',
+ '_type': 'partition',
+ 'some other key': 'some other value',
+ });
+ newTree.addChild(level1, treeNode);
+
+ const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+ expect(result).toEqual({
+ 'special one': {
+ 'some key': 'some value',
+ '_type': 'special one',
+ 'priority': -1,
+ },
+ 'table': {
+ 'some key': 'expected value',
+ 'some other key': 'some other value',
+ '_type': 'partition',
+ 'priority': 0,
+ },
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/tree/tree_fake.js b/web/regression/javascript/tree/tree_fake.js
index b285a45f..e03d71fb 100644
--- a/web/regression/javascript/tree/tree_fake.js
+++ b/web/regression/javascript/tree/tree_fake.js
@@ -10,6 +10,32 @@
import {Tree} from '../../../pgadmin/static/js/tree/tree';
export class TreeFake extends Tree {
+ static build(structure) {
+ let tree = new TreeFake();
+ let rootNode = tree.rootNode;
+
+ if (structure.children !== undefined) {
+ structure.children.forEach((child) => {
+ TreeFake.recursivelyAddNodes(tree, child, rootNode);
+ });
+ }
+
+ return tree;
+ }
+
+ static recursivelyAddNodes(tree, newNode, parent) {
+ let id = newNode.id;
+ let data = newNode.data ? newNode.data : {};
+ let domNode = newNode.domNode ? newNode.domNode : [{id: id}];
+ tree.addNewNode(id, data, domNode, tree.translateTreeNodeIdFromACITree([parent]));
+
+ if (newNode.children !== undefined) {
+ newNode.children.forEach((child) => {
+ TreeFake.recursivelyAddNodes(tree, child, newNode);
+ });
+ }
+ }
+
constructor() {
super();
this.aciTreeToOurTreeTranslator = {};
@@ -45,7 +71,7 @@ export class TreeFake extends Tree {
}
translateTreeNodeIdFromACITree(aciTreeNode) {
- if(aciTreeNode === undefined || aciTreeNode[0] === undefined) {
+ if (aciTreeNode === undefined || aciTreeNode[0] === undefined) {
return null;
}
return this.aciTreeToOurTreeTranslator[aciTreeNode[0].id];
diff --git a/web/webpack.shim.js b/web/webpack.shim.js
index 64f24336..c12e7f3b 100644
--- a/web/webpack.shim.js
+++ b/web/webpack.shim.js
@@ -130,6 +130,7 @@ var webpackShimConfig = {
'sources/utils': path.join(__dirname, './pgadmin/static/js/utils'),
'babel-polyfill': path.join(__dirname, './node_modules/babel-polyfill/dist/polyfill'),
'tools': path.join(__dirname, './pgadmin/tools/'),
+ 'pgbrowser': path.join(__dirname, './pgadmin/browser/static/js/'),
// Vendor JS
'jquery': path.join(__dirname, './node_modules/jquery/dist/jquery'),
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
index 1b374d62..e78e53e0 100644
--- a/web/webpack.test.config.js
+++ b/web/webpack.test.config.js
@@ -80,6 +80,8 @@ module.exports = {
'pgadmin.alertifyjs': sourcesDir + '/js/alertify.pgadmin.defaults',
'pgadmin.backgrid': sourcesDir + '/js/backgrid.pgadmin',
'pgadmin.backform': sourcesDir + '/js/backform.pgadmin',
+ 'pgbrowser': path.resolve(__dirname, 'regression/javascript/fake_browser'),
+ 'pgadmin.schema.dir': path.resolve(__dirname, 'pgadmin/browser/server_groups/servers/databases/schemas/static/js'),
},
},
};
view thread (69+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: [pgadmin4][patch] Initial patch to decouple from ACI Tree
In-Reply-To: <CAG7mmoxmwsd9GOtvAw-eT6=vT5JEx0VJYVf1H97Tk57khezOwA@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox