public inbox for [email protected]
help / color / mirror / Atom feedFrom: Ashesh Vashi <[email protected]>
To: Joao Pedro De Almeida Pereira <[email protected]>
Cc: Anthony Emengo <[email protected]>
Cc: Dave Page <[email protected]>
Cc: Akshay Joshi (EDB) <[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: Tue, 15 May 2018 16:07:08 +0530
Message-ID: <CAG7mmoxk6mzxxNm1YKU6=wKJ9KTAf4gKYX0y0AJxh6pS1h6HCw@mail.gmail.com> (raw)
In-Reply-To: <CAG7mmozzeu=oRrYv+QYR3u6jWUfj7rZMzTbF23w3cB7WEkY7fA@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>
Hi Joao,
On Mon, May 14, 2018 at 6:11 PM, Ashesh Vashi <[email protected]
> wrote:
>
> On Mon, May 14, 2018 at 6:10 PM, Dave Page <[email protected]> wrote:
>
>>
>>
>> On Mon, May 14, 2018 at 1:38 PM, Ashesh Vashi <
>> [email protected]> wrote:
>>
>>> On Mon, May 14, 2018 at 2:59 PM, Dave Page <[email protected]> wrote:
>>>
>>>>
>>>>
>>>> On Sat, May 12, 2018 at 12:10 AM, Ashesh Vashi <
>>>> [email protected]> wrote:
>>>>
>>>>> On Sat, May 12, 2018, 02:58 Joao De Almeida Pereira <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hello Ashesh,
>>>>>>
>>>>>> 1. In TreeNode, we're keepging the reference of DOMElement, do we
>>>>>>>> really need it?
>>>>>>>
>>>>>>> As of right now, our Tree abstraction acts as an adapter to the
>>>>>>>> aciTree library. The aciTree library needs the domElement for most of its
>>>>>>>> functions (setInode, unload, etc). Thus this is the easiest way to
>>>>>>>> introduce our abstraction and keep the functionality as before - at least
>>>>>>>> until we decide that whether we want to switch out the library or not.
>>>>>>>
>>>>>>> I understand that. But - I've not seen any reference of domElement
>>>>>>> the code yet, hence - pointed that out.
>>>>>>
>>>>>> If you look at the function: reload, unload you will see that domNode
>>>>>> is used to communicate with the ACITree
>>>>>>
>>>>>>
>>>>>>> 2. Are you expecting the tree class to be a singleton class
>>>>>>>
>>>>>>> Since this tree is referenced throughout the codebase, considering
>>>>>>>> it to be a singleton seems like the most appropriate pattern for this
>>>>>>>> usecase. It is very much the same way how we create a single instance of
>>>>>>>> the aciTree library and use that throughout the codebase. Moreover, it
>>>>>>>> opens up opportunities to improve performance, for example caching lockups
>>>>>>>> of nodes. I’m not a fan of singletons myself, but I feel like we’re simply
>>>>>>>> keeping the architecture the same in the instance.
>>>>>>>
>>>>>>> Yeah - I don't see any usage of tree object from anywhere.
>>>>>>> And, we're already creating new object in browser.js (and, not
>>>>>>> utitlizing that instance anywhere.)
>>>>>>
>>>>>>
>>>>>> You are right, we do not need to export tree as a singleton for now.
>>>>>> The line that exports the variable tree can be remove when applying
>>>>>> the patch number 2.
>>>>>>
>>>>>>
>>>>>> I think we addressed all the concern raised about this patch. Does
>>>>>> this mean that the patch is going to get committed?
>>>>>>
>>>>> Yes - from me for 0002.
>>>>>
>>>>
>>>> Can you do that today?
>>>>
>>> Done.
>>>
>>
>> Great, thanks!
>>
>> On to patch 0003 then :-)
>>
> Yes - already working on it! :-)
>
Majority part of the 0003 patch looks good to me.
Except choice of the path of some of the file, and name of the functions.
Please find the updated patch.
I've moved files under the 'pgadmin/static/js/menu' directory under the
'pgadmin/static/js/tree', as they're using tree functionalities directly.
Please review it, and let me know your concern.
-- Thanks, Ashesh
>
> -- Thanks, Ashesh
>
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>
>
Attachments:
[application/octet-stream] 0001-Extract-test-and-refactor-methods.patch (262.9K, 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..aefdbe41 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,9 @@
define('pgadmin.node.collation', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
+ 'sources/tree/node_menu',
'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser) {
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, treeNodeMenu) {
if (!pgBrowser.Nodes['coll-collation']) {
pgAdmin.Browser.Nodes['coll-collation'] =
@@ -222,34 +223,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-collation'),
});
}
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..ec6adb6c 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,11 @@
define('pgadmin.node.domain', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
+ 'sources/tree/node_menu',
'pgadmin.browser.collection',
], function(
- gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid
+ gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid,
+ treeNodeMenu
) {
// Define Domain Collection Node
@@ -296,34 +298,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-domain'),
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..4a8876cf 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,11 @@
define('pgadmin.node.foreign_table', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
+ 'sources/tree/node_menu',
'pgadmin.browser.collection',
], function(
- gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid
+ gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid,
+ treeNodeMenu
) {
if (!pgBrowser.Nodes['coll-foreign_table']) {
@@ -659,34 +661,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-foreign_table'),
});
}
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..06edc705 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,11 @@
define('pgadmin.node.fts_configuration', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
+ 'sources/tree/node_menu',
'pgadmin.browser.collection',
], function(
- gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid
+ gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid,
+ treeNodeMenu
) {
// Model for tokens control
@@ -577,34 +579,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-fts_configuration'),
});
}
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..2879b2be 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',
+ 'sources/tree/node_menu',
'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform) {
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform,
+ treeNodeMenu) {
// Extend the browser's node model class to create a option/value pair
var OptionLabelModel = pgAdmin.Browser.Node.Model.extend({
@@ -186,34 +188,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-fts_dictionary'),
});
}
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..abacdbc7 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,9 @@
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',
+ 'sources/tree/node_menu',
+ 'pgadmin.browser.collection',
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, treeNodeMenu) {
// Extend the collection class for fts parser
if (!pgBrowser.Nodes['coll-fts_parser']) {
@@ -199,34 +201,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-fts_parser'),
});
}
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..66d51c98 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,9 @@
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',
+ 'sources/tree/node_menu',
+ 'pgadmin.browser.collection',
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, treeNodeMenu) {
// Extend the collection class for fts template
if (!pgBrowser.Nodes['coll-fts_template']) {
@@ -139,34 +141,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-fts_template'),
});
}
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..af0a1f9f 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,10 @@
define('pgadmin.node.function', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
+ 'sources/tree/node_menu',
'pgadmin.browser.collection', 'pgadmin.browser.server.privilege',
-], function(gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform) {
+], function(gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform,
+ treeNodeMenu) {
if (!pgBrowser.Nodes['coll-function']) {
pgBrowser.Nodes['coll-function'] =
@@ -438,34 +440,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-function'),
});
}
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..ca552a1c 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,10 @@
define('pgadmin.node.trigger_function', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
+ 'sources/tree/node_menu',
'pgadmin.browser.collection', 'pgadmin.browser.server.privilege',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform) {
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform,
+ treeNodeMenu) {
if (!pgBrowser.Nodes['coll-trigger_function']) {
pgBrowser.Nodes['coll-trigger_function'] =
@@ -357,34 +359,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-trigger_function'),
});
}
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..44d5f6b3 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',
+ 'sources/tree/node_menu',
'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform) {
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform,
+ treeNodeMenu) {
// Extend the browser's collection class for sequence collection
if (!pgBrowser.Nodes['coll-sequence']) {
@@ -60,34 +62,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-sequence'),
// 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/can_drop_child.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/can_drop_child.js
new file mode 100644
index 00000000..84e25b5c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/can_drop_child.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 function canDropChild(pgBrowser, itemData, item) {
+ let node = pgBrowser.treeMenu.findNodeByDomElement(item);
+
+ if (node.anyParent((parent) => parent.getData()._type === 'catalog')) {
+ return false;
+ }
+
+ return true;
+}
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..a09610ad 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
@@ -1,8 +1,10 @@
define('pgadmin.node.schema', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
+ 'pgadmin.node.schema.dir/can_drop_child',
'pgadmin.browser.collection', 'pgadmin.browser.server.privilege',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid) {
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid,
+canDropChild) {
// VacuumSettings Collection to display all settings parameters as Grid
Backform.VacuumCollectionControl =
@@ -428,51 +430,12 @@ define('pgadmin.node.schema', [
// 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;
+ return canDropChild.canDropChild(pgBrowser, itemData, item);
},
});
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/tables/column/static/js/column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
index 72b6eb0c..1fdacf52 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',
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..2b687c1a 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
@@ -13,7 +13,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 +214,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',
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..d086473a 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',
+ 'sources/tree/node_menu',
'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,
+ treeNodeMenu
) {
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',
@@ -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: treeNodeMenu.canCreate(pgBrowser, 'coll-table'),
// 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..b0bc05b9 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',
+ 'sources/tree/node_menu',
'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, treeNodeMenu
) {
if (!pgBrowser.Nodes['coll-table']) {
@@ -26,7 +29,6 @@ define('pgadmin.node.table', [
if (!pgBrowser.Nodes['table']) {
pgBrowser.Nodes['table'] = pgBrowser.Node.extend({
- getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
type: 'table',
label: gettext('Table'),
collection_type: 'coll-table',
@@ -118,46 +120,21 @@ define('pgadmin.node.table', [
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,34 +1276,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-table'),
// 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/triggers/static/js/trigger.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
index a2c27188..4c25e3ea 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
@@ -29,14 +29,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',
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..28a223ad 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,9 @@
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', 'sources/tree/node_menu', 'pgadmin.browser.collection',
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid,
+ treeNodeMenu) {
if (!pgBrowser.Nodes['coll-type']) {
pgBrowser.Nodes['coll-type'] =
@@ -911,34 +912,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-type'),
});
}
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..ae3d55c1 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,10 @@
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', 'sources/tree/node_menu',
+ 'pgadmin.browser.server.privilege',
+], function(gettext, url_for, $, _, pgAdmin, Alertify, pgBrowser, Backform,
+ treeNodeMenu) {
/**
Create and add a view collection into nodes
@@ -240,39 +242,7 @@ 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;
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-mview'),
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..bd9e1fda 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,11 @@
define('pgadmin.node.view', [
'sources/gettext',
'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin',
- 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.browser.server.privilege',
+ 'pgadmin.browser', 'pgadmin.backform',
+ 'sources/tree/node_menu', 'pgadmin.browser.server.privilege',
'pgadmin.node.rule',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform) {
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform,
+ treeNodeMenu) {
/**
Create and add a view collection into nodes
@@ -202,40 +204,7 @@ define('pgadmin.node.view', [
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;
-
- },
+ canCreate: treeNodeMenu.canCreate(pgBrowser, 'coll-view'),
});
}
diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js
index ad582483..3baf554a 100644
--- a/web/pgadmin/browser/static/js/node.js
+++ b/web/pgadmin/browser/static/js/node.js
@@ -1,9 +1,12 @@
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 = {
@@ -1566,7 +1569,6 @@ define('pgadmin.browser.node', [
* depends, statistics
*/
generate_url: function(item, type, d, with_id, info) {
-
var opURL = {
'create': 'obj',
'drop': 'obj',
@@ -1608,24 +1610,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/tree/node_menu.js b/web/pgadmin/static/js/tree/node_menu.js
new file mode 100644
index 00000000..7aedb54e
--- /dev/null
+++ b/web/pgadmin/static/js/tree/node_menu.js
@@ -0,0 +1,46 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+/////////////////////////////////////////////////////////////
+
+export function canCreate(pgBrowser, childOfCatalogType) {
+ return canCreateObject.bind({
+ browser: pgBrowser,
+ childOfCatalogType: childOfCatalogType,
+ });
+}
+
+function canCreateObject(itemData, item, data) {
+ //If check is false then , we will allow create menu
+ if (data && data.check === false) {
+ return true;
+ }
+
+ let node = this.browser.treeMenu.findNodeByDomElement(item);
+
+ if (node.anyFamilyMember(
+ parentCatalogOfTableChild.bind(null, this.childOfCatalogType)
+ )) {
+ return false;
+ }
+
+ return true;
+}
+
+function parentCatalogOfTableChild(arg, node) {
+ if (arg === node.getData()._type) {
+ if (node.hasParent()) {
+
+ let parent = node.parent();
+ if ('catalog' === parent.getData()._type) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
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/supported_nodes_menu.js b/web/pgadmin/static/js/tree/supported_nodes_menu.js
new file mode 100644
index 00000000..8cdfcd98
--- /dev/null
+++ b/web/pgadmin/static/js/tree/supported_nodes_menu.js
@@ -0,0 +1,50 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {isValidTreeNodeData} from './tree';
+
+function isNodeTypeSupported(supportedNodes, nodeDataType, treeNode) {
+ return _.indexOf(supportedNodes, nodeDataType) !== -1
+ && ancestorWithTypeCatalogDoesNotExists(treeNode);
+}
+
+function doesNodeHaveMenu(treeNodeData) {
+ return (treeNodeData._type === 'database' && treeNodeData.allowConn)
+ || treeNodeData._type !== 'database';
+}
+
+function ancestorWithTypeCatalogDoesNotExists(treeNode) {
+ let currentNode = treeNode;
+
+ while(currentNode.hasParent() && treeNode.parent().getData() !== null) {
+ if(currentNode.parent().getData()._type === 'catalog') {
+ return false;
+ }
+
+ currentNode = currentNode.parent();
+ }
+
+ return true;
+}
+
+export function enabled(tree, supportedNodes, treeNodeData, domTreeNode) {
+ let treeNode = tree.findNodeByDomElement(domTreeNode);
+ if (!treeNode) {
+ return false;
+ }
+
+ if (isValidTreeNodeData(treeNodeData)) {
+ return isNodeTypeSupported(supportedNodes, treeNodeData._type, treeNode)
+ && doesNodeHaveMenu(treeNodeData);
+ } else {
+ return false;
+ }
+}
+
+
diff --git a/web/pgadmin/static/js/tree/tree.js b/web/pgadmin/static/js/tree/tree.js
index 01edb6c3..a8b122cf 100644
--- a/web/pgadmin/static/js/tree/tree.js
+++ b/web/pgadmin/static/js/tree/tree.js
@@ -210,3 +210,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..fa4e5c6d 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/tree/supported_nodes_menu',
], function(
gettext, url_for, $, _, S, alertify, Backbone, Backgrid, Backform, pgBrowser,
-commonUtils
+commonUtils, menuUtils, globalBackupDialog, supportedNodesMenu
) {
// 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: supportedNodesMenu.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: supportedNodesMenu.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..1b9e6db5 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/tree/supported_nodes_menu',
+ 'backgrid.select.all',
'backgrid.filter', 'pgadmin.browser.server.privilege',
'pgadmin.browser.wizard',
], function(
gettext, url_for, $, _, Backbone, Alertify, Backgrid, Backform, pgBrowser,
- pgNode
+ pgNode, menuUtils, supportedNodesMenu
) {
// 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: supportedNodesMenu.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: supportedNodesMenu.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..1ede8536 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/tree/supported_nodes_menu',
+ 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui',
], function(
gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
-Backform, commonUtils
+Backform, commonUtils, supportedNodesMenu
) {
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: supportedNodesMenu.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..5f703c6d 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/tree/supported_nodes_menu',
'pgadmin.backform', 'pgadmin.backgrid',
'pgadmin.browser.node.ui',
], function(
gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
- Backform, commonUtils
+ Backform, commonUtils,
+ menuUtils, supportedNodesMenu
) {
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: supportedNodesMenu.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: supportedNodesMenu.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..bf4a34ca 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/tree/supported_nodes_menu',
+ 'tools/restore/static/js/restore_dialog',
], function(
gettext, url_for, $, _, Backbone, S, alertify, pgBrowser, Backgrid, Backform,
-commonUtils
+commonUtils, menuUtils, supportedNodesMenu, 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: supportedNodesMenu.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: supportedNodesMenu.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_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/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/schema/can_drop_child_spec.js b/web/regression/javascript/schema/can_drop_child_spec.js
new file mode 100644
index 00000000..4403b274
--- /dev/null
+++ b/web/regression/javascript/schema/can_drop_child_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 {canDropChild} from '../../../pgadmin/browser/server_groups/servers/databases/schemas/static/js/can_drop_child';
+import {TreeFake} from '../tree/tree_fake';
+
+let context = describe;
+
+describe('can_drop_child', () => {
+
+ let browser;
+ let itemData;
+ let item;
+
+ beforeEach(() => {
+ browser = {
+ treeMenu: new TreeFake(),
+ };
+ item = [];
+ browser.treeMenu.addNewNode('node1', {_type: 'schema'}, [{id: 'node1'}], []);
+ browser.treeMenu.addNewNode('node1.1', {_type: 'database'}, [{id: 'node1.1'}], ['node1']);
+ browser.treeMenu.addNewNode('node2', {_type: 'catalog'}, [{id: 'node2'}], []);
+ browser.treeMenu.addNewNode('node2.1', {_type: 'table'}, [{id: 'node2.1'}], ['node2']);
+ browser.treeMenu.addNewNode('node3', {_type: 'function'}, [{id: 'node3'}], []);
+ browser.treeMenu.addNewNode('node3.1', {_type: 'procedure'}, [{id: 'node3.1'}], ['node3']);
+ });
+
+ context('when current node is of the type schema', () => {
+ beforeEach(() => {
+ itemData = {
+ _type: 'schema',
+ };
+ item = [{id: 'node1'}];
+ });
+
+ it('returns true', () => {
+ let bool = canDropChild(browser, itemData, item);
+ expect(bool).toBe(true);
+ });
+ });
+
+ context('when a parent of the current node is a schema', () => {
+ it('returns true', () => {
+ itemData = {
+ _type: 'database',
+ };
+ item = [{id: 'node1.1'}];
+ let bool = canDropChild(browser, itemData, item);
+ expect(bool).toBe(true);
+ });
+ });
+
+ context('when a parent of the current node is a catalog', () => {
+ it('returns false', () => {
+ itemData= {
+ _type: 'table',
+ };
+ item = [{id: 'node2.1'}];
+
+ let bool = canDropChild(browser, itemData, item);
+ expect(bool).toBe(false);
+ });
+ });
+
+ context('when a parent of the current node is not catalog nor schema', () => {
+ it('returns true', () => {
+ itemData = {
+ _type: 'procedure',
+ };
+ item = [{id: 'node3.1'}];
+
+ let bool = canDropChild(browser, itemData, item);
+ expect(bool).toBe(true);
+ });
+ });
+});
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/can_create_spec.js b/web/regression/javascript/tree/can_create_spec.js
new file mode 100644
index 00000000..3008d8ff
--- /dev/null
+++ b/web/regression/javascript/tree/can_create_spec.js
@@ -0,0 +1,167 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+/////////////////////////////////////////////////////////////
+
+import {canCreate} from '../../../pgadmin/static/js/tree/node_menu';
+import {TreeFake} from '../tree/tree_fake';
+
+const context = describe;
+
+describe('#canCreate', () => {
+ let ourBrowser;
+ let data;
+ let tree;
+ let createCheck;
+
+ context('data is not null and check is false ', () => {
+ beforeEach(() => {
+ data = {action: 'create', check: false};
+ });
+ it('returns true', () => {
+ createCheck = canCreate({}, {});
+ expect(createCheck({}, {}, data)).toBe(true);
+ });
+ });
+
+ context('data is not null and check is true', () => {
+ beforeEach(() => {
+ data = {action: 'create', check: true};
+ });
+
+ context('is node with type schema', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [
+ {
+ id: 'level2',
+ data: {_type: 'schema'},
+ },
+ ],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ ourBrowser = {treeMenu: tree};
+ });
+
+ it('returns true', () => {
+ createCheck = canCreate(ourBrowser, 'coll-table');
+ expect(createCheck({}, [{id: 'level2'}], data)).toBe(true);
+ });
+ });
+
+ context('has ancestor with type schema', () => {
+ beforeEach(() => {
+
+ let hierarchy = {
+ id: 'root',
+ children: [
+ {
+ id: 'level2',
+ data: {_type: 'schema'},
+ children: [
+ {
+ id: 'level3',
+ data: {_type: 'database'},
+ },
+ ],
+ },
+ ],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ ourBrowser = {treeMenu: tree};
+ });
+
+ it('returns true', () => {
+ createCheck = canCreate(ourBrowser, 'coll-table');
+ expect(createCheck(null, [{id: 'level3'}], data)).toBe(true);
+ });
+ });
+
+ context('when type is not "coll-table"', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [
+ {
+ id: 'level2',
+ data: {_type: 'database'},
+ },
+ ],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ ourBrowser = {treeMenu: tree};
+ });
+
+ it('returns true', () => {
+ createCheck = canCreate(ourBrowser, 'coll-table');
+ expect(createCheck(null, [{id: 'level2'}], data)).toBe(true);
+ });
+ });
+
+ context('when type is "coll-table"', () => {
+ context('when parent type is "catalog"', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [
+ {
+ id: 'level2',
+ data: {_type: 'catalog'},
+ children: [
+ {
+ id: 'level3',
+ data: {_type: 'coll-table'},
+ },
+ ],
+ },
+ ],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ ourBrowser = {treeMenu: tree};
+ });
+
+ it('returns false', () => {
+ createCheck = canCreate(ourBrowser, 'coll-table');
+ expect(createCheck(null, [{id: 'level3'}], data)).toBe(false);
+ });
+ });
+
+ context('when parent type is not "catalog"', () => {
+ beforeEach(() => {
+ let hierarchy = {
+ id: 'root',
+ children: [
+ {
+ id: 'level2',
+ data: {_type: 'database'},
+ children: [
+ {
+ id: 'level3',
+ data: {_type: 'coll-table'},
+ },
+ ],
+ },
+ ],
+ };
+
+ tree = TreeFake.build(hierarchy);
+ ourBrowser = {treeMenu: tree};
+ });
+
+ it('returns false', () => {
+ createCheck = canCreate(ourBrowser, 'coll-table');
+ expect(createCheck(null, [{id: 'level3'}], data)).toBe(true);
+ });
+ });
+ });
+ });
+});
diff --git a/web/regression/javascript/tree/menu_enabled_spec.js b/web/regression/javascript/tree/menu_enabled_spec.js
new file mode 100644
index 00000000..ac0af8e7
--- /dev/null
+++ b/web/regression/javascript/tree/menu_enabled_spec.js
@@ -0,0 +1,131 @@
+/////////////////////////////////////////////////////////////
+//
+// 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 {enabled as menuEnabled} from '../../../pgadmin/static/js/tree/supported_nodes_menu';
+
+const context = describe;
+describe('#menuEnabled', () => {
+ let ourBrowser;
+ beforeEach(() => {
+ const tree = new TreeFake();
+ ourBrowser = {
+ treeMenu: tree,
+ };
+ tree.addNewNode('level1', {}, undefined, []);
+ tree.addNewNode('level1.1', {_type: 'catalog'}, undefined, ['level1']);
+ tree.addNewNode('level1.1.1', {_type: 'database'}, undefined, ['level1', 'level1.1']);
+ tree.addNewNode('level1.2', {_type: 'bamm'}, undefined, ['level1']);
+ tree.addNewNode('level1.2.1', {
+ _type: 'database',
+ allowConn: true,
+ }, undefined, ['level1', 'level1.2']);
+ tree.addNewNode('level1.2.2', {
+ _type: 'database',
+ allowConn: false,
+ }, undefined, ['level1', 'level1.2']);
+ tree.addNewNode('level1.2.3', {
+ _type: 'table',
+ }, undefined, ['level1', 'level1.2']);
+
+ tree.addNewNode('level2', {}, undefined, []);
+ tree.addNewNode('level2.1', null, undefined, ['level2']);
+ tree.addNewNode('level2.1.1', {}, undefined, ['level2', 'level2.1']);
+ });
+
+ context('When the current node is a root node', () => {
+ it('return false', () => {
+ expect(menuEnabled(ourBrowser.treeMenu, [], {}, [{id: 'level1'}])).toBe(false);
+ });
+ });
+
+ context('when current node does not exist', () => {
+ it('return false', () => {
+ expect(menuEnabled(ourBrowser.treeMenu, [], {}, [{id: 'bamm'}])).toBe(false);
+ });
+ });
+
+ context('provided node data is undefined', () => {
+ it('returns false', () => {
+ expect(menuEnabled(ourBrowser.treeMenu, [], undefined, [{id: 'level1'}])).toBe(false);
+ });
+ });
+
+ context('provided node data is null', () => {
+ it('returns false', () => {
+ expect(menuEnabled(ourBrowser.treeMenu, [], null, [{id: 'level1'}])).toBe(false);
+ });
+ });
+
+ context('When the current node is not a root node', () => {
+ context('parent data does not exist', () => {
+ it('returns false', () => {
+ expect(menuEnabled(ourBrowser.treeMenu, [], {}, [{id: 'level2.1.1'}])).toBe(false);
+ });
+ });
+
+ context('parent as data', () => {
+ context('the current node type is in the supported node types', () => {
+ context('the parent is of the type catalog', () => {
+ it('returns false', () => {
+ expect(menuEnabled(ourBrowser.treeMenu,
+ ['schema'],
+ {_type: 'schema'},
+ [{id: 'level1.1.1'}]
+ )).toBe(false);
+ });
+ });
+ context('an ancestor with type catalog exists', () => {
+ it('returns false', () => {
+ expect(menuEnabled(ourBrowser.treeMenu,
+ ['table'],
+ {_type: 'table'},
+ [{id: 'level1.1.1.1'}]
+ )).toBe(false);
+ });
+ });
+ context('the parent is not of the type catalog', () => {
+ context('current node is of the type database', () => {
+ context('current node allows connection', () => {
+ it('returns true', () => {
+ expect(menuEnabled(ourBrowser.treeMenu,
+ ['database'],
+ {
+ _type: 'database',
+ allowConn: true,
+ }, [{id: 'level1.2.1'}])).toBe(true);
+ });
+ });
+ context('current node do not allow connection', () => {
+ it('returns false', () => {
+ expect(menuEnabled(ourBrowser.treeMenu,
+ ['database'], {
+ _type: 'database',
+ allowConn: false,
+ }, [{id: 'level1.2.2'}])).toBe(false);
+ });
+ });
+ });
+ context('current node is not of the type database', () => {
+ it('returns true', () => {
+ expect(menuEnabled(ourBrowser.treeMenu,
+ ['schema'], {
+ _type: 'schema',
+ }, [{id: 'level1.2.3'}])).toBe(true);
+ });
+ });
+ });
+ });
+ context('the current node type is not in the supported node types', () => {
+ it('returns false', () => {
+ expect(menuEnabled(ourBrowser.treeMenu, [], {_type: 'catalog'}, [{id: 'level1.1'}])).toBe(false);
+ });
+ });
+ });
+ });
+});
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];
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: <CAG7mmoxk6mzxxNm1YKU6=wKJ9KTAf4gKYX0y0AJxh6pS1h6HCw@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