public inbox for [email protected]  
help / color / mirror / Atom feed
From: Ashesh Vashi <[email protected]>
To: Victoria Henry <[email protected]>
Cc: Anthony Emengo <[email protected]>
Cc: Joao De Almeida Pereira <[email protected]>
Cc: Dave Page <[email protected]>
Cc: Akshay Joshi <[email protected]>
Cc: Murtuza Zabuawala <[email protected]>
Cc: pgadmin-hackers <[email protected]>
Cc: Khushboo Vashi <[email protected]>
Subject: Re: [pgadmin4][patch] Initial patch to decouple from ACI Tree
Date: Sat, 2 Jun 2018 00:06:26 +0530
Message-ID: <CAG7mmox1C7XM23wKhhG0ok8wkKhh2QHy1EsNrmKJS3eR29KCMg@mail.gmail.com> (raw)
In-Reply-To: <CANxYE3KtK8Ot=ddQHChD3it74uoQjOcPUS23=YkW+5Lzo3V60Q@mail.gmail.com>
References: <CAE+jja=Gdd032H7tpoZD2C0m2R7SnTZpHX_oPx2K2zGbaaW9yg@mail.gmail.com>
	<CAFOhELcrZsRkwV4beOC7UT_HYvi9Rk=HFJ355g8Bj4bqyzMLog@mail.gmail.com>
	<CAE+jjaneGOR_R7QvYnZt=khumRsyhnrzFRhpMG5-Q+rR9guLzQ@mail.gmail.com>
	<CAKKotZROcoO_rtrrJUTvpfgSh1w4ig1ogQrkDrKSQHuKEL7CdQ@mail.gmail.com>
	<CAE+jjakEZtn0ct128Xd+es5xCZ5eZLwoNZw6X0ue7oFB+a83oQ@mail.gmail.com>
	<CAFOhELe_t_QGBbcYYm3Zrfwf3z9674PY5sy2QS46wHDTf-8DHA@mail.gmail.com>
	<CAE+jja=u+W+FNaWGVs1KPZm1eYA_F=7ktomSbmsxOo-cjuOdBw@mail.gmail.com>
	<CAE+jja=TZzMXOq4+t=dA+LfTGkW66urB37Tt9sEJ_UjAiymbbQ@mail.gmail.com>
	<CAE+jjamSM_pBLNmYLn6-hWMiQvToceV2iPuME-eH5c+tgaYf7A@mail.gmail.com>
	<CA+OCxowN15jg_DV4o+R+OSczyGHHccnJ1sPXE_XukYugaTKyyw@mail.gmail.com>
	<CA+OCxozTMcEDKZEu9YCOsSBSed23Ka8hHiO5s6VF76KopbipNQ@mail.gmail.com>
	<CAG7mmowrM3b8cTY+zcCAcVma8CKWHwXSF3=BUDcSFLpjH=jQBg@mail.gmail.com>
	<CAE+jjanjQ295x0DgffT8OARRv8fge5KOPJpMCnLWe+j0jdqGJQ@mail.gmail.com>
	<CAG7mmoym9838PVKdDD=469Bsx6AFBV1ThMK_dyitUmG26147Qg@mail.gmail.com>
	<CAG7mmoy=5W3scnPs-TVpcu49F7gZU2qySPaakwfEdcg6rg2RMQ@mail.gmail.com>
	<CAG8BBZOAFgw6iDseWTSEUC+tiwc0zCoQyycXGFWbW6ueftCggA@mail.gmail.com>
	<CAG7mmoyirqdDGZnXcz5odMBnsOYY83Dj6Uw8gV59OPTJSnaMAg@mail.gmail.com>
	<CA+OCxoz3C+HRDzh05SKhMjSZPnHezf=F0eiNoo_HHqUtiJWPiQ@mail.gmail.com>
	<CAG7mmoxjfZGA8Gp4HMByTAf6ZLZ7Oaj2yTUrgbOrpJ0H7OtQNQ@mail.gmail.com>
	<CAE+jja=GFVqodrWJ+vzBYO4tzoZipKqdThKUqMOGyoZPtJc1+Q@mail.gmail.com>
	<CAG7mmozE+8pneLOp8RokHZiav0kcM2Ts6NkHwrz2n31-Ykn_kA@mail.gmail.com>
	<CA+OCxowNhQ7waRSPp5P--dFisLFkf+EEOwH+y0+t+ZT9dXLXTA@mail.gmail.com>
	<CAE+jjamkYzDxjvSmdkU-1Gyys1FXtrXGr+d7=yrsAREmXsteOQ@mail.gmail.com>
	<CAE+jja=vaQOE6XbJH34+N24CZY1DRdYz8wgkxxTx5KSaqw7pLQ@mail.gmail.com>
	<CAE+jjakrO-RaK=m5YYUBrF=9SgYjOyU59bZo+vWxDoFXG75Jvg@mail.gmail.com>
	<CAE+jjanc7WbL6k42=Dp8NBw9mdiv4jccK7t5JfN2rD2n8NiYTg@mail.gmail.com>
	<CAFOhELfyS404C50JWdgV16sBO7WYAJYYOD80kBA1AEhREALCmw@mail.gmail.com>
	<CANxoLDfyOFt2QDAXE5BVC6X8tHZV4q3K=_gRiuWN_ife9FYU_w@mail.gmail.com>
	<CAE+jjamBMWfWCypF_h5x0NThhmGHx7to6HsJ66WSmJrL6UdQ+g@mail.gmail.com>
	<CAG7mmoyjWCTZUXH-CBTpgNy4ErA_6ojRgd+dT63zdAkGMY_2JQ@mail.gmail.com>
	<CAG8BBZOKQDKtyu1-2HVCP5HFy1rKFdPVrMTY0SPEhb8x7Q7PBA@mail.gmail.com>
	<CAG7mmowkNtkoLGQFeWAH-TSOi+QWbHT2-RbMmjRDAMAxMufmeg@mail.gmail.com>
	<CAE+jja=PLKUdA43dAz2exgLcy6n5EGbM0E4K1ZdKmi5hex7Rew@mail.gmail.com>
	<CAG7mmoyxBz+D+933L9UzgQrmRgn3xHxHuW06fwsNr-WkgXD_8w@mail.gmail.com>
	<CA+OCxoz3eSNJ25sy_AmvERjNp24pD=Dfq-3a4ZQrmBVedDf-4g@mail.gmail.com>
	<CAG7mmoxqA-Wa4hi0nwwg9nQKo1zmvYwfLrnGvVJjmobsNCCYmQ@mail.gmail.com>
	<CA+OCxoz_V+z3WrfsbxXWNYcFHCqaURRwiJb6R_N1Yn0PWZcd8A@mail.gmail.com>
	<CAG7mmozzeu=oRrYv+QYR3u6jWUfj7rZMzTbF23w3cB7WEkY7fA@mail.gmail.com>
	<CAG7mmoxk6mzxxNm1YKU6=wKJ9KTAf4gKYX0y0AJxh6pS1h6HCw@mail.gmail.com>
	<CAE+jjakrT2AhM2gEGt+QKEANMypn08O4vweOgwhyqQdV6mZEEQ@mail.gmail.com>
	<CAG7mmowDFFbPFSxn0swpM2Ub_W0x842Yz=qiBmAC8ZxPYtn+Rw@mail.gmail.com>
	<CAG8BBZNndOje9T-B=Jrxj0tqJbDYc489yYbkJkJx_V5LKzsM-w@mail.gmail.com>
	<CAG7mmoxmwsd9GOtvAw-eT6=vT5JEx0VJYVf1H97Tk57khezOwA@mail.gmail.com>
	<CAE+jjandjswWj030ULtEvX9dnwTLzCdJ=NY82H7BHPEQjRNsfQ@mail.gmail.com>
	<CAG7mmozES8twvhLDKDJfHTp1F+xvvOt4qWmjXNTsPqJnPY=mjg@mail.gmail.com>
	<CAG8BBZPXP9Z+NuQ20S_cctjrudZYbCDahu-4aFLpyh+RBeuUwA@mail.gmail.com>
	<CANxYE3KtK8Ot=ddQHChD3it74uoQjOcPUS23=YkW+5Lzo3V60Q@mail.gmail.com>

On Fri, Jun 1, 2018 at 10:09 PM, Victoria Henry <[email protected]> wrote:

> Hi Ashesh,
>
> We just attempted to apply your patch over master but it did not work.  We
> don't want to introduce any bugs or break any functionality.  Please update
> the patch to make sure it is synced up with the master branch.
>
Please find the updated patch.

>
> Sincerely,
>
> Victoria
>
> On Fri, Jun 1, 2018 at 11:18 AM Anthony Emengo <[email protected]> wrote:
>
>> Hey Ashesh,
>>
>> Thanks for the explanation. It was great and it really helped!
>>
>> C pgadmin/browser/server_groups/servers/databases/schemas/static/js/child.js
>> C pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema_child_tree_node.js
>>
>> It makes sense to remove duplication by extracting these attributes out
>> and setting the canDrop and canCreate functions here. But is it possible
>> to combine these two files into one since they are related so we don’t need
>> to import schema_child_tree_node?
>>
> That was the original plan, but 'pgadmin/browser/static/js//node.js'
script has too many dependecies, which are not easily portable.
And - that may lead to change the scope of the patch.

Hence - I decided to use the separate file to make sure we have enough test
coverage (which is more imprortant than changing the scope).

> M pgadmin/static/js/tree/tree.js
>>
>> The creation of the ancestorNode function feels like a pre-optimization.
>> That function is not used any where outside of the tree.js file, so it’s
>> more confusing to have it defined.
>>
> It is being used in the latest changes. :-)


> On a lighter note, could we avoid the !! syntax when possible? For
>> example, instead of return !!obj, we could do something like return obj
>> === undefined or return _.isUndefined(obj) as this is more intuitive.
>>
>> https://softwareengineering.stackexchange.com/a/80092
>>
> I am kind of disagree here. But - I have changed it anyway.

> In addition, please update this patch as it is out of sync with the latest
>> commit on the master branch. Otherwise, everything looks good!
>>
> Here - you go!

-- Thanks, Ashesh

> ​
>>
>> Thanks
>> Anthony && Victoria
>>
>> On Fri, Jun 1, 2018 at 7:52 AM Ashesh Vashi <
>> [email protected]> wrote:
>>
>>> On Thu, May 24, 2018 at 8:13 PM, Joao De Almeida Pereira <
>>> [email protected]> wrote:
>>>
>>>> Hey, Thanks so much for the reply.
>>>>
>>>> We've noticed that you've made several modifications on top of our
>>>> original patch. Unfortunately, we've found it very hard to follow. Could we
>>>> please get a brief synopsis of the changes you have made - just so we can
>>>> better understand the rationale behind them? Just like we've done for you
>>>> previously.
>>>>
>>> Please find the changes from your original patch:
>>>
>>> M webpack.shim.js
>>> M webpack.test.config.js
>>> - In order to specify the fake_browser in regression tests, we need to use 'pgbrowser/browser' in the 'schema_child_tree_node.js' script.D pgadmin/browser/server_groups/servers/databases/schemas/static/js/can_drop_child.js
>>> - We don't need this with the new implementation.C pgadmin/browser/server_groups/servers/databases/schemas/static/js/child.js
>>> - All the children of schema node have common properties as 'parent_type', 'canDrop', 'canDropCascase', 'canCreate'.
>>>   Hence - instead of defining them in each node, we have created a base node, which will have all these properties.
>>>   And, modified all schema children node to inherit from it.C pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema_child_tree_node.js
>>> - In this script, we're defining three functions 'childCreateMenuEnabled', 'isTreeItemOfChildOfSchema', & 'isTreeNodeOfSchemaChild', which are used by the 'SchemaChildNode' objects.M pgadmin/browser/static/js/collection.js
>>> - Fixed an issue related to wrongly defined 'error' function for the Collection object.D pgadmin/static/js/menu/can_create.js
>>> - It defined the function, which was defining a check for creation of a schema child node, or not by looking at the parent node (i.e. a schema/catalog node).
>>>   The file was not defintely placed under the wrong directory, because - the similar logic was under 'can_drop_child.js', and it was defined under 'pgadmin/browser/server_groups/servers/databases/schemas/static/js' directory.D pgadmin/static/js/menu/menu_enabled.jsC pgadmin/static/js/nodes/supported_database_node.js
>>> - Used by the external tools for checking whether the 'selected' tree-node is:
>>>   + 'database' node, and it is allowed to connect it.
>>>   + Or, it is one of the schema child (and, not 'catalog' child).
>>> - Finding the correct location was difficult for this, as there is no defined pattern, also it can be used by other functions too. Hence - moved it out of 'pgadmin/static/js/menu' directory.M pgadmin/static/js/tree/tree.js
>>> - Introduced a function, which returns the ancestor node object, fow which the condition is true.D regression/javascript/menu/can_create_spec.js
>>> D regression/javascript/menu/menu_enabled_spec.js
>>> D regression/javascript/schema/can_drop_child_spec.jsC regression/javascript/fake_browser/browser.js
>>> C regression/javascript/nodes/schema/child_menu_spec.js
>>> - Modified the regression to test the new functionalies.M pgadmin/browser/server_groups/servers/databases/schemas/**/*.js
>>> - Extending the schema child nodes from the 'SchemaChildNode' class defined in 'pgadmin/.../schemas/static/js/child.js' script.
>>>
>>> Let me know if you need more information.
>>>
>>>
>>>> Let's keep in mind that the original intent was simply to introduce
>>>> this abstraction into the code base, which is a big enough task. I'd hate
>>>> for the scope of the changes we're making to expand beyond that.
>>>>
>>>
>>> I have the mutual feeling.
>>>
>>> -- Thanks, Ashesh
>>>
>>>>
>>>> Thanks
>>>> Joao && Anthony
>>>>
>>>>
>>>> On Thu, May 24, 2018 at 2:59 AM Ashesh Vashi <
>>>> [email protected]> wrote:
>>>>
>>>>> Sorry for the late reply.
>>>>> On Wed, May 16, 2018 at 8:55 PM, Anthony Emengo <[email protected]>
>>>>> wrote:
>>>>>
>>>>>> export function canCreate(pgBrowser, childOfCatalogType) {
>>>>>>   return canCreateObject.bind({
>>>>>>     browser: pgBrowser,
>>>>>>     childOfCatalogType: childOfCatalogType,
>>>>>>   });
>>>>>> }
>>>>>>
>>>>>> With respect to the above code: this bind pattern looks good and
>>>>>> seems like the idiomatic way to handle this in JavaScript. On a lighter
>>>>>> node, I don’t even see the need for an additional method to wrap it. The
>>>>>> invocation could have easily been like canCreate:
>>>>>> canCreateObject.bind({ browser: pgBrowser, childOfCatalogType:
>>>>>> childOfCatalogType }), I don’t feel too strongly here.
>>>>>>
>>>>> I do agree - we can handle the same problem many ways.
>>>>> I prefer object oriented pardigm more in general.
>>>>> Any way - I have modified the code with some other changes.
>>>>>
>>>>>> I renamed it as isValidTreeNodeData, because - we were using it in
>>>>>> for testing the tree data. Please suggest me the right place, and name.
>>>>>>
>>>>>> We’re not sure; maybe after continued refactoring, we will come
>>>>>> across more generic functions. At that point we can revisit this and create
>>>>>> a utils.js file.
>>>>>>
>>>>> Sure.
>>>>>
>>>>>> The original patch was separating them in different places, but -
>>>>>> still uses some of the functionalities directly from the tree, which was
>>>>>> happening because we have contextual menu.
>>>>>> To give a better solution, I can think of putting the menus related
>>>>>> code understand ‘sources/tree/menu’ directory.
>>>>>>
>>>>>> We’re particularly worried because we’re trying to avoid the coupling
>>>>>> that we see in the code base today. We want to decouple *application
>>>>>> state* from *business domain* logic as much as we can - because this
>>>>>> makes the code much easier to understand. We achieve lower coupling by have
>>>>>> more suitable interfaces to retrieve *application state* like:
>>>>>> anyParent (the menu doesn’t care how this happens). This is the
>>>>>> direction that we’re trying to move towards, we just don’t want the package
>>>>>> structure to undermine that developer intent.
>>>>>>
>>>>> I realized after revisiting the code, menu/can_create.js was only
>>>>> applicable to the children of the schema/catalog nodes, same as
>>>>> 'can_drop_child'.
>>>>> We should have put both scripts in the same directory.
>>>>>
>>>>> Please find the updated patch for the same.
>>>>>
>>>>> Please review it, and let me know your concerns.
>>>>>
>>>>> -- Thanks, Ashesh
>>>>>
>>>>>> How about nodeMenu.isSupportedNode(…)?
>>>>>>
>>>>>> Naming is one of the hardest problems in programming. I don’t feel
>>>>>> too strongly about this one. For now, let’s keep it as is
>>>>>>
>>>>>> Thanks
>>>>>> Anthony && Victoria
>>>>>> ​
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>


Attachments:

  [application/octet-stream] 0001-Extract-test-and-refactor-methods_v2.patch (292.1K, 3-0001-Extract-test-and-refactor-methods_v2.patch)
  download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js
index 9015d8d2..7fd28a7c 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js
@@ -1,8 +1,8 @@
 define('pgadmin.node.collation', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
-  'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser) {
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, schemaChild) {
 
   if (!pgBrowser.Nodes['coll-collation']) {
     pgAdmin.Browser.Nodes['coll-collation'] =
@@ -15,7 +15,7 @@ define('pgadmin.node.collation', [
   }
 
   if (!pgBrowser.Nodes['collation']) {
-    pgAdmin.Browser.Nodes['collation'] = pgBrowser.Node.extend({
+    pgAdmin.Browser.Nodes['collation'] = schemaChild.SchemaChildNode.extend({
       type: 'collation',
       sqlAlterHelp: 'sql-altercollation.html',
       sqlCreateHelp: 'sql-createcollation.html',
@@ -222,34 +222,6 @@ define('pgadmin.node.collation', [
           return true;
         },
       }),
-      canCreate: function(itemData, item, data) {
-          //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-          // To iterate over tree to check parent node
-        while (i) {
-            // If it is schema then allow user to create collation
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-collation' == d._type) {
-              //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-          // by default we do not want to allow create menu
-        return true;
-      },
     });
 
   }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js
index 403ca471..a91daa5f 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js
@@ -2,9 +2,10 @@
 define('pgadmin.node.domain', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
   'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
-  'pgadmin.browser.collection',
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
 ], function(
-  gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid
+  gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid,
+  schemaChild
 ) {
 
   // Define Domain Collection Node
@@ -79,7 +80,7 @@ define('pgadmin.node.domain', [
 
   // Domain Node
   if (!pgBrowser.Nodes['domain']) {
-    pgBrowser.Nodes['domain'] = pgBrowser.Node.extend({
+    pgBrowser.Nodes['domain'] = schemaChild.SchemaChildNode.extend({
       type: 'domain',
       sqlAlterHelp: 'sql-alterdomain.html',
       sqlCreateHelp: 'sql-createdomain.html',
@@ -88,7 +89,6 @@ define('pgadmin.node.domain', [
       collection_type: 'coll-domain',
       hasSQL: true,
       hasDepends: true,
-      parent_type: ['schema', 'catalog'],
       Init: function() {
         // Avoid mulitple registration of menus
         if (this.initialized)
@@ -118,8 +118,6 @@ define('pgadmin.node.domain', [
         ]);
 
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
       // Domain Node Model
       model: pgBrowser.Node.Model.extend({
         initialize: function(attrs, args) {
@@ -296,34 +294,6 @@ define('pgadmin.node.domain', [
           return errmsg;
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create domain
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-domain' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
       isDisabled: function(m){
         if (!m.isNew()) {
           var server = this.node_info.server;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js
index 160db83f..24f5e1a7 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js
@@ -2,9 +2,10 @@
 define('pgadmin.node.foreign_table', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
   'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
-  'pgadmin.browser.collection',
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
 ], function(
-  gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid
+  gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid,
+  schemaChild
 ) {
 
   if (!pgBrowser.Nodes['coll-foreign_table']) {
@@ -469,7 +470,7 @@ define('pgadmin.node.foreign_table', [
 
 
   if (!pgBrowser.Nodes['foreign_table']) {
-    pgBrowser.Nodes['foreign_table'] = pgBrowser.Node.extend({
+    pgBrowser.Nodes['foreign_table'] = schemaChild.SchemaChildNode.extend({
       type: 'foreign_table',
       sqlAlterHelp: 'sql-alterforeigntable.html',
       sqlCreateHelp: 'sql-createforeigntable.html',
@@ -509,8 +510,6 @@ define('pgadmin.node.foreign_table', [
         ]);
 
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
       model: pgBrowser.Node.Model.extend({
         initialize: function(attrs, args) {
           var isNew = (_.size(attrs) === 0);
@@ -659,34 +658,6 @@ define('pgadmin.node.foreign_table', [
           return errmsg;
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create foreign table
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-foreign_table' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
     });
 
   }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js
index 89806681..cba62789 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js
@@ -1,9 +1,10 @@
 define('pgadmin.node.fts_configuration', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
   'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid',
-  'pgadmin.browser.collection',
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
 ], function(
-  gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid
+  gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid,
+  schemaChild
 ) {
 
   // Model for tokens control
@@ -410,14 +411,11 @@ define('pgadmin.node.fts_configuration', [
 
   // Extend the node class for FTS Configuration
   if (!pgBrowser.Nodes['fts_configuration']) {
-    pgAdmin.Browser.Nodes['fts_configuration'] = pgAdmin.Browser.Node.extend({
-      parent_type: ['schema', 'catalog'],
+    pgAdmin.Browser.Nodes['fts_configuration'] = schemaChild.SchemaChildNode.extend({
       type: 'fts_configuration',
       sqlAlterHelp: 'sql-altertsconfig.html',
       sqlCreateHelp: 'sql-createtsconfig.html',
       dialogHelp: url_for('help.static', {'filename': 'fts_configuration_dialog.html'}),
-      canDrop: true,
-      canDropCascade: true,
       label: gettext('FTS Configuration'),
       hasSQL: true,
       hasDepends: true,
@@ -577,34 +575,6 @@ define('pgadmin.node.fts_configuration', [
           return null;
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create fts configuration
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-fts_configuration' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
     });
   }
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js
index ed83feb1..cf733922 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js
@@ -1,8 +1,10 @@
 define('pgadmin.node.fts_dictionary', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
-  'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform) {
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+], function(
+  gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, schemaChild
+) {
 
   // Extend the browser's node model class to create a option/value pair
   var OptionLabelModel = pgAdmin.Browser.Node.Model.extend({
@@ -55,14 +57,11 @@ define('pgadmin.node.fts_dictionary', [
 
   // Extend the node class for FTS Dictionary
   if (!pgBrowser.Nodes['fts_dictionary']) {
-    pgAdmin.Browser.Nodes['fts_dictionary'] = pgAdmin.Browser.Node.extend({
-      parent_type: ['schema', 'catalog'],
+    pgAdmin.Browser.Nodes['fts_dictionary'] = schemaChild.SchemaChildNode.extend({
       type: 'fts_dictionary',
       sqlAlterHelp: 'sql-altertsdictionary.html',
       sqlCreateHelp: 'sql-createtsdictionary.html',
       dialogHelp: url_for('help.static', {'filename': 'fts_dictionary_dialog.html'}),
-      canDrop: true,
-      canDropCascade: true,
       label: gettext('FTS Dictionary'),
       hasSQL: true,
       hasDepends: true,
@@ -186,34 +185,6 @@ define('pgadmin.node.fts_dictionary', [
           return null;
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create fts dictionary
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-fts_dictionary' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
     });
   }
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js
index 92c0786e..bebc9dc5 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js
@@ -1,7 +1,8 @@
 define('pgadmin.node.fts_parser', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
-  'sources/pgadmin', 'pgadmin.browser', 'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser) {
+  'sources/pgadmin', 'pgadmin.browser', 'pgadmin.node.schema.dir/child',
+  'pgadmin.browser.collection',
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, schemaChild) {
 
   // Extend the collection class for fts parser
   if (!pgBrowser.Nodes['coll-fts_parser']) {
@@ -16,14 +17,11 @@ define('pgadmin.node.fts_parser', [
 
   // Extend the node class for fts parser
   if (!pgBrowser.Nodes['fts_parser']) {
-    pgAdmin.Browser.Nodes['fts_parser'] = pgAdmin.Browser.Node.extend({
-      parent_type: ['schema', 'catalog'],
+    pgAdmin.Browser.Nodes['fts_parser'] = schemaChild.SchemaChildNode.extend({
       type: 'fts_parser',
       sqlAlterHelp: 'sql-altertsparser.html',
       sqlCreateHelp: 'sql-createtsparser.html',
       dialogHelp: url_for('help.static', {'filename': 'fts_parser_dialog.html'}),
-      canDrop: true,
-      canDropCascade: true,
       label: gettext('FTS Parser'),
       hasSQL: true,
       hasDepends: true,
@@ -199,34 +197,6 @@ define('pgadmin.node.fts_parser', [
           return null;
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create fts parser
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-fts_parser' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
     });
   }
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js
index 606a57a6..cd0207ab 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js
@@ -1,7 +1,8 @@
 define('pgadmin.node.fts_template', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
-  'sources/pgadmin', 'pgadmin.browser', 'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser) {
+  'sources/pgadmin', 'pgadmin.browser', 'pgadmin.node.schema.dir/child',
+  'pgadmin.browser.collection',
+], function(gettext, url_for, $, _, pgAdmin, pgBrowser, schemaChild) {
 
   // Extend the collection class for fts template
   if (!pgBrowser.Nodes['coll-fts_template']) {
@@ -16,14 +17,11 @@ define('pgadmin.node.fts_template', [
 
   // Extend the node class for fts template
   if (!pgBrowser.Nodes['fts_template']) {
-    pgAdmin.Browser.Nodes['fts_template'] = pgAdmin.Browser.Node.extend({
-      parent_type: ['schema', 'catalog'],
+    pgAdmin.Browser.Nodes['fts_template'] = schemaChild.SchemaChildNode.extend({
       type: 'fts_template',
       sqlAlterHelp: 'sql-altertstemplate.html',
       sqlCreateHelp: 'sql-createtstemplate.html',
       dialogHelp: url_for('help.static', {'filename': 'fts_template_dialog.html'}),
-      canDrop: true,
-      canDropCascade: true,
       label: gettext('FTS Template'),
       hasSQL: true,
       hasDepends: true,
@@ -139,34 +137,6 @@ define('pgadmin.node.fts_template', [
           return null;
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create fts fts_template
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-fts_template' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
     });
   }
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js
index 6e405165..c4cd91aa 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js
@@ -2,8 +2,11 @@
 define('pgadmin.node.function', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
   'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
-  'pgadmin.browser.collection', 'pgadmin.browser.server.privilege',
-], function(gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform) {
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+  'pgadmin.browser.server.privilege',
+], function(
+  gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, schemaChild
+) {
 
   if (!pgBrowser.Nodes['coll-function']) {
     pgBrowser.Nodes['coll-function'] =
@@ -83,7 +86,8 @@ define('pgadmin.node.function', [
   });
 
   if (!pgBrowser.Nodes['function']) {
-    pgBrowser.Nodes['function'] = pgBrowser.Node.extend({
+
+    pgBrowser.Nodes['function'] = schemaChild.SchemaChildNode.extend({
       type: 'function',
       sqlAlterHelp: 'sql-alterfunction.html',
       sqlCreateHelp: 'sql-createfunction.html',
@@ -96,7 +100,6 @@ define('pgadmin.node.function', [
         return treeInformation.server.server_type !== 'gpdb';
       },
       hasScriptTypes: ['create', 'select'],
-      parent_type: ['schema', 'catalog'],
       Init: function() {
         /* Avoid mulitple registration of menus */
         if (this.initialized)
@@ -126,8 +129,6 @@ define('pgadmin.node.function', [
         ]);
 
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
       model: pgBrowser.Node.Model.extend({
         initialize: function(attrs, args) {
           var isNew = (_.size(attrs) === 0);
@@ -438,34 +439,6 @@ define('pgadmin.node.function', [
           return true;
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create Function
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-function' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
     });
 
   }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js
index aeb8271b..fcdf28fb 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js
@@ -2,8 +2,11 @@
 define('pgadmin.node.trigger_function', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
-  'pgadmin.browser.collection', 'pgadmin.browser.server.privilege',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform) {
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+  'pgadmin.browser.server.privilege',
+], function(
+  gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, schemaChild
+) {
 
   if (!pgBrowser.Nodes['coll-trigger_function']) {
     pgBrowser.Nodes['coll-trigger_function'] =
@@ -17,7 +20,7 @@ define('pgadmin.node.trigger_function', [
   }
 
   if (!pgBrowser.Nodes['trigger_function']) {
-    pgBrowser.Nodes['trigger_function'] = pgBrowser.Node.extend({
+    pgBrowser.Nodes['trigger_function'] = schemaChild.SchemaChildNode.extend({
       type: 'trigger_function',
       sqlAlterHelp: 'plpgsql-trigger.html',
       sqlCreateHelp: 'plpgsql-trigger.html',
@@ -27,7 +30,6 @@ define('pgadmin.node.trigger_function', [
       hasSQL: true,
       hasDepends: true,
       hasStatistics: true,
-      parent_type: ['schema', 'catalog'],
       Init: function() {
         /* Avoid mulitple registration of menus */
         if (this.initialized)
@@ -57,8 +59,6 @@ define('pgadmin.node.trigger_function', [
         ]);
 
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
       model: pgBrowser.Node.Model.extend({
         initialize: function(attrs, args) {
           var isNew = (_.size(attrs) === 0);
@@ -357,34 +357,6 @@ define('pgadmin.node.trigger_function', [
           return !(this.node_info &&  'catalog' in this.node_info);
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create Function
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-trigger_function' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
     });
 
   }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js
index 57c95acd..300f0b10 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js
@@ -1,8 +1,10 @@
 define('pgadmin.node.sequence', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
-  'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform) {
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+], function(
+  gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, schemaChild
+) {
 
   // Extend the browser's collection class for sequence collection
   if (!pgBrowser.Nodes['coll-sequence']) {
@@ -18,7 +20,7 @@ define('pgadmin.node.sequence', [
 
   // Extend the browser's node class for sequence node
   if (!pgBrowser.Nodes['sequence']) {
-    pgBrowser.Nodes['sequence'] = pgBrowser.Node.extend({
+    pgBrowser.Nodes['sequence'] = schemaChild.SchemaChildNode.extend({
       type: 'sequence',
       sqlAlterHelp: 'sql-altersequence.html',
       sqlCreateHelp: 'sql-createsequence.html',
@@ -28,7 +30,6 @@ define('pgadmin.node.sequence', [
       hasSQL: true,
       hasDepends: true,
       hasStatistics: true,
-      parent_type: ['schema', 'catalog'],
       Init: function() {
         /* Avoid mulitple registration of menus */
         if (this.initialized)
@@ -58,36 +59,6 @@ define('pgadmin.node.sequence', [
         ]);
 
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
-      canCreate: function(itemData, item, data) {
-          //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-          // To iterate over tree to check parent node
-        while (i) {
-            // If it is schema then allow user to create collation
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-sequence' == d._type) {
-              //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-          // by default we want to allow create menu
-        return true;
-      },
       // Define the model for sequence node.
       model: pgBrowser.Node.Model.extend({
         defaults: {
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/child.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/child.js
new file mode 100644
index 00000000..f8e5951c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/child.js
@@ -0,0 +1,22 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+/////////////////////////////////////////////////////////////
+
+import * as Node from 'pgbrowser/node';
+import {
+  isTreeItemOfChildOfSchema, childCreateMenuEnabled,
+} from './schema_child_tree_node';
+
+let SchemaChildNode = Node.extend({
+  parent_type: ['schema', 'catalog'],
+  canDrop: isTreeItemOfChildOfSchema,
+  canDropCascade: isTreeItemOfChildOfSchema,
+  canCreate: childCreateMenuEnabled,
+}, false);
+
+export {SchemaChildNode};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js
index a7fd4c7c..3b9b0f35 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js
@@ -425,54 +425,10 @@ define('pgadmin.node.schema', [
           return null;
         },
       }),
-      // This function will checks whether we can allow user to
-      // drop object or not based on location within schema & catalog
-      canChildDrop: function(itemData, item) {
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create collation
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-            //Check if we are not child of catalog
-          var prev_i = t.hasParent(i) ? t.parent(i) : null,
-            prev_d = prev_i ? t.itemData(prev_i) : null;
-          if(prev_d && prev_d._type == 'catalog') {
-            return false;
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
     });
 
     pgBrowser.tableChildTreeNodeHierarchy = function(i) {
-      var idx = 0,
-        res = {},
-        t = pgBrowser.tree;
-
-      do {
-        var d = t.itemData(i);
-        if (
-          d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId
-        ) {
-          if (d._type === 'partition' || d._type === 'table') {
-            if (!('table' in res)) {
-              res['table'] = _.extend({}, d, {'priority': idx});
-              idx -= 1;
-            }
-          } else {
-            res[d._type] = _.extend({}, d, {'priority': idx});
-            idx -= 1;
-          }
-        }
-        i = t.hasParent(i) ? t.parent(i) : null;
-      } while (i);
-
-      return res;
+      return this.getTreeNodeHierarchy(i);
     };
   }
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema_child_tree_node.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema_child_tree_node.js
new file mode 100644
index 00000000..1f67d1af
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema_child_tree_node.js
@@ -0,0 +1,41 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+/////////////////////////////////////////////////////////////
+
+import * as pgBrowser from 'pgbrowser/browser';
+
+export function childCreateMenuEnabled(itemData, item, data) {
+  // If check is false then , we will allow create menu
+  if (data && data.check === false) {
+    return true;
+  }
+
+  let node = pgBrowser.treeMenu.findNodeByDomElement(item);
+
+  if (node)
+    return node.anyFamilyMember(
+      (node) => (node.getData()._type === 'schema')
+    );
+
+  return false;
+}
+
+export function isTreeItemOfChildOfSchema(itemData, item) {
+  let node = pgBrowser.treeMenu.findNodeByDomElement(item);
+
+  if (node)
+    return isTreeNodeOfSchemaChild(node);
+
+  return false;
+}
+
+export function isTreeNodeOfSchemaChild(node) {
+  return node.anyParent(
+    (parentNode) => (parentNode.getData()._type === 'schema')
+  );
+}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
index 3eac530d..582167a4 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
@@ -88,7 +88,6 @@ define('pgadmin.node.column', [
 
   if (!pgBrowser.Nodes['column']) {
     pgBrowser.Nodes['column'] = pgBrowser.Node.extend({
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       parent_type: ['table', 'view', 'mview'],
       collection_type: ['coll-table', 'coll-view', 'coll-mview'],
       type: 'column',
@@ -97,27 +96,24 @@ define('pgadmin.node.column', [
       sqlAlterHelp: 'sql-altertable.html',
       sqlCreateHelp: 'sql-altertable.html',
       dialogHelp: url_for('help.static', {'filename': 'column_dialog.html'}),
-      canDrop: function(itemData, item, data){
-        if (pgBrowser.Nodes['schema'].canChildDrop.apply(this, [itemData, item, data])) {
-          var t = pgBrowser.tree, i = item, d = itemData, parents = [];
-          // To iterate over tree to check parent node
-          while (i) {
-            parents.push(d._type);
-            i = t.hasParent(i) ? t.parent(i) : null;
-            d = i ? t.itemData(i) : null;
-          }
+      canDrop: function(itemData, item){
+        let node = pgBrowser.treeMenu.findNodeByDomElement(item);
 
-          // Check if menu is allowed ?
-          if(_.indexOf(parents, 'catalog') > -1 ||
-             _.indexOf(parents, 'view') > -1 ||
-             _.indexOf(parents, 'mview') > -1) {
-            return false;
-          } else if(_.indexOf(parents, 'table') > -1) {
-            return true;
-          }
-        } else {
+        if (!node)
           return false;
-        }
+
+        // Only a column of a table can be droped, and only when it is not of
+        // catalog.
+        return node.anyParent(
+          (parentNode) => (
+            parentNode.getData()._type === 'table' &&
+              !parentNode.anyParent(
+                (grandParentNode) => (
+                  grandParentNode.getData()._type === 'catalog'
+                )
+              )
+          )
+        );
       },
       hasDepends: true,
       hasStatistics: true,
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js
index 857cf4c4..ab28a86b 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js
@@ -8,7 +8,6 @@ define('pgadmin.node.check_constraint', [
   // Check Constraint Node
   if (!pgBrowser.Nodes['check_constraint']) {
     pgAdmin.Browser.Nodes['check_constraint'] = pgBrowser.Node.extend({
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       type: 'check_constraint',
       label: gettext('Check'),
       collection_type: 'coll-constraints',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js
index 0bbf66a1..adccf2e9 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js
@@ -605,7 +605,6 @@ define('pgadmin.node.exclusion_constraint', [
   // Extend the browser's node class for exclusion constraint node
   if (!pgBrowser.Nodes['exclusion_constraint']) {
     pgAdmin.Browser.Nodes['exclusion_constraint'] = pgBrowser.Node.extend({
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       type: 'exclusion_constraint',
       label: gettext('Exclusion constraint'),
       collection_type: 'coll-constraints',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
index 3c4b89f3..9899df92 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
@@ -603,7 +603,6 @@ define('pgadmin.node.foreign_key', [
   // Extend the browser's node class for foreign key node
   if (!pgBrowser.Nodes['foreign_key']) {
     pgAdmin.Browser.Nodes['foreign_key'] = pgBrowser.Node.extend({
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       type: 'foreign_key',
       label: gettext('Foreign key'),
       collection_type: 'coll-constraints',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js
index d3a6cff4..0ad0f054 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js
@@ -20,7 +20,6 @@ define('pgadmin.node.primary_key', [
       parent_type: ['table','partition'],
       canDrop: true,
       canDropCascade: true,
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       Init: function() {
         /* Avoid multiple registration of menus */
         if (this.initialized)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js
index 769185d6..18d3ca33 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js
@@ -20,7 +20,6 @@ define('pgadmin.node.unique_constraint', [
       parent_type: ['table','partition'],
       canDrop: true,
       canDropCascade: true,
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       Init: function() {
         /* Avoid multiple registration of menus */
         if (this.initialized)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js
index cb242cd2..9c0e24fe 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js
@@ -12,14 +12,12 @@ define('pgadmin.node.constraints', [
         node: 'constraints',
         label: gettext('Constraints'),
         type: 'coll-constraints',
-        getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
         columns: ['name', 'comment'],
       });
   }
 
   if (!pgBrowser.Nodes['constraints']) {
     pgAdmin.Browser.Nodes['constraints'] = pgBrowser.Node.extend({
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       type: 'constraints',
       label: gettext('Constraints'),
       collection_type: 'coll-constraints',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
index ec2b4da1..e58bb463 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
@@ -1,10 +1,12 @@
 define('pgadmin.node.index', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'backbone', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.alertifyjs',
-  'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.collection',
+  'pgadmin.backform', 'pgadmin.backgrid',
+  'pgadmin.node.schema.dir/schema_child_tree_node',
+  'pgadmin.browser.collection',
 ], function(
   gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Alertify, Backform,
-  Backgrid
+  Backgrid, SchemaChildTreeNode
 ) {
 
   if (!pgBrowser.Nodes['coll-index']) {
@@ -13,7 +15,6 @@ define('pgadmin.node.index', [
         node: 'index',
         label: gettext('Indexes'),
         type: 'coll-index',
-        getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
         sqlAlterHelp: 'sql-alterindex.html',
         sqlCreateHelp: 'sql-createindex.html',
         dialogHelp: url_for('help.static', {'filename': 'index_dialog.html'}),
@@ -215,7 +216,6 @@ define('pgadmin.node.index', [
 
   if (!pgBrowser.Nodes['index']) {
     pgAdmin.Browser.Nodes['index'] = pgBrowser.Node.extend({
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       parent_type: ['table', 'view', 'mview', 'partition'],
       collection_type: ['coll-table', 'coll-view'],
       sqlAlterHelp: 'sql-alterindex.html',
@@ -266,8 +266,8 @@ define('pgadmin.node.index', [
         },
         ]);
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      canDrop: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
+      canDropCascade: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
       model: pgAdmin.Browser.Node.Model.extend({
         idAttribute: 'oid',
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js
index d807304e..74649721 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js
@@ -2,10 +2,12 @@ define([
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
   'pgadmin.alertifyjs', 'pgadmin.backform', 'pgadmin.backgrid',
+  'pgadmin.node.schema.dir/schema_child_tree_node',
   'pgadmin.browser.collection', 'pgadmin.browser.table.partition.utils',
 ],
 function(
-  gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, Backform, Backgrid
+  gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, Backform, Backgrid,
+  SchemaChildTreeNode
 ) {
 
   if (!pgBrowser.Nodes['coll-partition']) {
@@ -13,7 +15,6 @@ function(
       pgAdmin.Browser.Collection.extend({
         node: 'partition',
         label: gettext('Partitions'),
-        getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
         type: 'coll-partition',
         columns: [
           'name', 'schema', 'partition_value', 'is_partitioned', 'description',
@@ -80,36 +81,6 @@ function(
         },
         ]);
       },
-      getTreeNodeHierarchy: function(i) {
-        var idx = 0,
-          res = {},
-          t = pgBrowser.tree;
-
-        do {
-          var d = t.itemData(i);
-          if (
-            d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId
-          ) {
-            if (d._type == 'partition' && 'partition' in res) {
-              if (!('table' in res)) {
-                res['table'] = _.extend({}, d, {'priority': idx});
-                idx -= 1;
-              }
-            } else if (d._type == 'table') {
-              if (!('table' in res)) {
-                res['table'] = _.extend({}, d, {'priority': idx});
-                idx -= 1;
-              }
-            } else {
-              res[d._type] = _.extend({}, d, {'priority': idx});
-              idx -= 1;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-        } while (i);
-
-        return res;
-      },
       generate_url: function(item, type, d, with_id, info) {
         if (_.indexOf([
           'stats', 'statistics', 'dependency', 'dependent', 'reset',
@@ -133,8 +104,8 @@ function(
             encodeURIComponent(info['partition']._id)
             ).value();
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      canDrop: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
+      canDropCascade: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
       callbacks: {
         /* Enable trigger(s) on table */
         enable_triggers_on_table: function(args) {
@@ -1189,34 +1160,7 @@ function(
           return data;
         },
       }),
-      canCreate: function(itemData, item, data) {
-          //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-          // To iterate over tree to check parent node
-        while (i) {
-            // If it is schema then allow user to create table
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-table' == d._type) {
-              //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null;
-            var prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-          // by default we do not want to allow create menu
-        return true;
-      },
+      canCreate: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
       // Check to whether table has disable trigger(s)
       canCreate_with_trigger_enable: function(itemData, item, data) {
         if(this.canCreate.apply(this, [itemData, item, data])) {
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js
index 3af61754..354909f3 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/static/js/rule.js
@@ -16,7 +16,6 @@ define('pgadmin.node.rule', [
         node: 'rule',
         label: gettext('Rules'),
         type: 'coll-rule',
-        getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
         columns: ['name', 'owner', 'comment'],
       });
   }
@@ -35,7 +34,6 @@ define('pgadmin.node.rule', [
    */
   if (!pgBrowser.Nodes['rule']) {
     pgAdmin.Browser.Nodes['rule'] = pgBrowser.Node.extend({
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       parent_type: ['table','view', 'partition'],
       type: 'rule',
       sqlAlterHelp: 'sql-alterrule.html',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/enable_disable_triggers.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/enable_disable_triggers.js
new file mode 100644
index 00000000..2d792043
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/enable_disable_triggers.js
@@ -0,0 +1,52 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import axios from 'axios';
+
+export function disableTriggers(tree, alertify, generateUrl, args) {
+  return setTriggers(tree, alertify, generateUrl, args, {enable: 'false' });
+}
+export function enableTriggers(tree, alertify, generateUrl, args) {
+  return setTriggers(tree, alertify, generateUrl, args, {enable: 'true' });
+}
+
+function setTriggers(tree, alertify, generateUrl, args, params) {
+  const treeNode = retrieveTreeNode(args, tree);
+
+  if (!treeNode || treeNode.getData() === null || treeNode.getData() === undefined)
+    return false;
+
+  axios.put(
+    generateUrl(treeNode.getHtmlIdentifier(), 'set_trigger', treeNode.getData(), true),
+    params
+  )
+    .then((res) => {
+      if (res.data.success === 1) {
+        alertify.success(res.data.info);
+        treeNode.reload(tree);
+      }
+    })
+    .catch((xhr) => {
+      try {
+        const err = xhr.response.data;
+        if (err.success === 0) {
+          alertify.error(err.errormsg);
+        }
+      } catch (e) {
+        console.warn(e.stack || e);
+      }
+      treeNode.unload(tree);
+    });
+}
+
+function retrieveTreeNode(args, tree) {
+  const input = args || {};
+  const domElementIdentifier = input.item || tree.selected();
+  return tree.findNodeByDomElement(domElementIdentifier);
+}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js
index d440bf04..1f9bcf97 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js
@@ -1,13 +1,16 @@
 define('pgadmin.node.table', [
+  'pgadmin.tables.js/enable_disable_triggers',
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
   'pgadmin.alertifyjs', 'pgadmin.backform', 'pgadmin.backgrid',
   'pgadmin.tables.js/show_advanced_tab',
-  'pgadmin.browser.collection', 'pgadmin.node.column',
-  'pgadmin.node.constraints', 'pgadmin.browser.table.partition.utils',
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection',
+  'pgadmin.node.column', 'pgadmin.node.constraints',
+  'pgadmin.browser.table.partition.utils',
 ], function(
+  tableFunctions,
   gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, Backform, Backgrid,
-  ShowAdvancedTab
+  ShowAdvancedTab, SchemaChild
 ) {
 
   if (!pgBrowser.Nodes['coll-table']) {
@@ -25,8 +28,7 @@ define('pgadmin.node.table', [
   }
 
   if (!pgBrowser.Nodes['table']) {
-    pgBrowser.Nodes['table'] = pgBrowser.Node.extend({
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
+    pgBrowser.Nodes['table'] = SchemaChild.SchemaChildNode.extend({
       type: 'table',
       label: gettext('Table'),
       collection_type: 'coll-table',
@@ -39,7 +41,6 @@ define('pgadmin.node.table', [
       sqlAlterHelp: 'sql-altertable.html',
       sqlCreateHelp: 'sql-createtable.html',
       dialogHelp: url_for('help.static', {'filename': 'table_dialog.html'}),
-      parent_type: ['schema', 'catalog'],
       hasScriptTypes: ['create', 'select', 'insert', 'update', 'delete'],
       height: '95%',
       width: '85%',
@@ -113,51 +114,24 @@ define('pgadmin.node.table', [
           this.handle_cache, this
         );
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
       callbacks: {
         /* Enable trigger(s) on table */
         enable_triggers_on_table: function(args) {
-          var params = {'enable': true };
-          this.callbacks.set_triggers.apply(this, [args, params]);
+          tableFunctions.enableTriggers(
+            pgBrowser.treeMenu,
+            Alertify,
+            this.generate_url.bind(this),
+            args
+          );
         },
         /* Disable trigger(s) on table */
         disable_triggers_on_table: function(args) {
-          var params = {'enable': false };
-          this.callbacks.set_triggers.apply(this, [args, params]);
-        },
-        set_triggers: function(args, params) {
-          // This function will send request to enable or
-          // disable triggers on table level
-          var input = args || {},
-            obj = this,
-            t = pgBrowser.tree,
-            i = input.item || t.selected(),
-            d = i && i.length == 1 ? t.itemData(i) : undefined;
-          if (!d)
-            return false;
-
-          $.ajax({
-            url: obj.generate_url(i, 'set_trigger' , d, true),
-            type:'PUT',
-            data: params,
-            dataType: 'json',
-            success: function(res) {
-              if (res.success == 1) {
-                Alertify.success(res.info);
-                t.unload(i);
-                t.setInode(i);
-                t.deselect(i);
-                setTimeout(function() {
-                  t.select(i);
-                }, 10);
-              }
-            },
-            error: function(xhr, status, error) {
-              Alertify.pgRespErrorNotify(xhr, error);
-              t.unload(i);
-            },
-          });
+          tableFunctions.disableTriggers(
+            pgBrowser.treeMenu,
+            Alertify,
+            this.generate_url.bind(this),
+            args
+          );
         },
         /* Truncate table */
         truncate_table: function(args) {
@@ -1299,55 +1273,15 @@ define('pgadmin.node.table', [
           return data;
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create table
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-table' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
       // Check to whether table has disable trigger(s)
       canCreate_with_trigger_enable: function(itemData, item, data) {
-        if(this.canCreate.apply(this, [itemData, item, data])) {
-          // We are here means we can create menu, now let's check condition
-          if(itemData.tigger_count > 0) {
-            return true;
-          } else {
-            return false;
-          }
-        }
+        return itemData.tigger_count > 0 &&
+          this.canCreate.apply(this, [itemData, item, data]);
       },
       // Check to whether table has enable trigger(s)
       canCreate_with_trigger_disable: function(itemData, item, data) {
-        if(this.canCreate.apply(this, [itemData, item, data])) {
-          // We are here means we can create menu, now let's check condition
-          if(itemData.tigger_count > 0 && itemData.has_enable_triggers > 0) {
-            return true;
-          } else {
-            return false;
-          }
-        }
+        return itemData.tigger_count > 0 && itemData.has_enable_triggers > 0 &&
+          this.canCreate.apply(this, [itemData, item, data]);
       },
       onTableUpdated: function(_node, _oldNodeData, _newNodeData) {
         var key, childIDs;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
index a2c27188..a6e79ce2 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
@@ -1,8 +1,13 @@
 define('pgadmin.node.trigger', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
-  'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'backform', 'pgadmin.alertifyjs',
+  'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
+  'pgadmin.backform', 'pgadmin.alertifyjs',
+  'pgadmin.node.schema.dir/schema_child_tree_node',
   'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, alertify) {
+], function(
+  gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, alertify,
+  SchemaChildTreeNode
+) {
 
   Backform.CustomSwitchControl = Backform.SwitchControl.extend({
     template: _.template([
@@ -29,14 +34,12 @@ define('pgadmin.node.trigger', [
         node: 'trigger',
         label: gettext('Triggers'),
         type: 'coll-trigger',
-        getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
         columns: ['name', 'description'],
       });
   }
 
   if (!pgBrowser.Nodes['trigger']) {
     pgAdmin.Browser.Nodes['trigger'] = pgBrowser.Node.extend({
-      getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
       parent_type: ['table', 'view', 'partition'],
       collection_type: ['coll-table', 'coll-view'],
       type: 'trigger',
@@ -175,8 +178,8 @@ define('pgadmin.node.trigger', [
           });
         },
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      canDrop: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
+      canDropCascade: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
       model: pgAdmin.Browser.Node.Model.extend({
         defaults: {
           name: undefined,
@@ -618,50 +621,16 @@ define('pgadmin.node.trigger', [
           return flag;
         },
       }),
-      // Below function will enable right click menu for creating column
-      canCreate: function(itemData, item, data) {
-        // If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData, parents = [];
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to c reate table
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-          parents.push(d._type);
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // If node is under catalog then do not allow 'create' menu
-        if (_.indexOf(parents, 'catalog') > -1) {
-          return false;
-        } else {
-          return true;
-        }
-      },
+      canCreate: SchemaChildTreeNode.isTreeItemOfChildOfSchema,
       // Check to whether trigger is disable ?
       canCreate_with_trigger_enable: function(itemData, item, data) {
-        if(this.canCreate.apply(this, [itemData, item, data])) {
-          // We are here means we can create menu, now let's check condition
-          if(itemData.icon === 'icon-trigger-bad') {
-            return true;
-          } else {
-            return false;
-          }
-        }
+        return itemData.icon === 'icon-trigger-bad' &&
+          this.canCreate.apply(this, [itemData, item, data]);
       },
       // Check to whether trigger is enable ?
       canCreate_with_trigger_disable: function(itemData, item, data) {
-        if(this.canCreate.apply(this, [itemData, item, data])) {
-          // We are here means we can create menu, now let's check condition
-          if(itemData.icon === 'icon-trigger') {
-            return true;
-          } else {
-            return false;
-          }
-        }
+        return itemData.icon === 'icon-trigger' &&
+          this.canCreate.apply(this, [itemData, item, data]);
       },
     });
   }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js
index c1c24861..5860a752 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js
@@ -1,8 +1,12 @@
 define('pgadmin.node.type', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
-  'pgadmin.backgrid', 'pgadmin.browser.collection',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid) {
+  'pgadmin.backgrid', 'pgadmin.node.schema.dir/child',
+  'pgadmin.browser.collection',
+], function(
+  gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid,
+  schemaChild
+) {
 
   if (!pgBrowser.Nodes['coll-type']) {
     pgBrowser.Nodes['coll-type'] =
@@ -245,7 +249,7 @@ define('pgadmin.node.type', [
   });
 
   if (!pgBrowser.Nodes['type']) {
-    pgBrowser.Nodes['type'] = pgBrowser.Node.extend({
+    pgBrowser.Nodes['type'] = schemaChild.SchemaChildNode.extend({
       type: 'type',
       sqlAlterHelp: 'sql-altertype.html',
       sqlCreateHelp: 'sql-createtype.html',
@@ -254,7 +258,6 @@ define('pgadmin.node.type', [
       collection_type: 'coll-type',
       hasSQL: true,
       hasDepends: true,
-      parent_type: ['schema', 'catalog'],
       Init: function() {
         /* Avoid multiple registration of menus */
         if (this.initialized)
@@ -284,8 +287,6 @@ define('pgadmin.node.type', [
         ]);
 
       },
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
       ext_funcs: undefined,
       model: pgBrowser.Node.Model.extend({
         defaults: {
@@ -911,34 +912,6 @@ define('pgadmin.node.type', [
           return result;
         },
       }),
-      canCreate: function(itemData, item, data) {
-        //If check is false then , we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to create table
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-type' == d._type) {
-            //Check if we are not child of catalog
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      },
     });
   }
   return pgBrowser.Nodes['type'];
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js
index 073ef5cb..dcfdd54b 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js
@@ -1,8 +1,12 @@
 define('pgadmin.node.mview', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'sources/pgadmin', 'pgadmin.alertifyjs', 'pgadmin.browser',
-  'pgadmin.backform', 'pgadmin.browser.server.privilege',
-], function(gettext, url_for, $, _, pgAdmin, Alertify, pgBrowser, Backform) {
+  'pgadmin.backform', 'pgadmin.node.schema.dir/child',
+  'pgadmin.browser.server.privilege',
+], function(
+  gettext, url_for, $, _, pgAdmin, Alertify, pgBrowser, Backform,
+  schemaChild
+) {
 
   /**
     Create and add a view collection into nodes
@@ -33,19 +37,16 @@ define('pgadmin.node.mview', [
     view option in the context menu
    */
   if (!pgBrowser.Nodes['mview']) {
-    pgBrowser.Nodes['mview'] = pgBrowser.Node.extend({
-      parent_type: ['schema', 'catalog'],
+    pgBrowser.Nodes['mview'] = schemaChild.SchemaChildNode.extend({
       type: 'mview',
       sqlAlterHelp: 'sql-altermaterializedview.html',
       sqlCreateHelp: 'sql-creatematerializedview.html',
       dialogHelp: url_for('help.static', {'filename': 'materialized_view_dialog.html'}),
       label: gettext('Materialized View'),
-      hasSQL:  true,
+      hasSQL: true,
       hasDepends: true,
       hasScriptTypes: ['create', 'select'],
       collection_type: 'coll-mview',
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
       Init: function() {
 
         // Avoid mulitple registration of menus
@@ -236,43 +237,6 @@ define('pgadmin.node.mview', [
 
       }),
 
-      /**
-        Show or hide create view menu option on parent node
-        and hide for system view in catalogs.
-       */
-      canCreate: function(itemData, item, data) {
-
-        // If check is false then, we will allow create menu
-        if (data && data.check === false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-
-        // To iterate over tree to check parent node
-        while (i) {
-
-          // If it is schema then allow user to create view
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-mview' == d._type) {
-
-            // Check if we are not child of view
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-
-        // by default we do not want to allow create menu
-        return true;
-      },
       refresh_mview: function(args) {
         var input = args || {},
           obj = this,
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js
index 5755a509..cd61ef21 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js
@@ -1,9 +1,12 @@
 define('pgadmin.node.view', [
-  'sources/gettext',
-  'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin',
-  'pgadmin.browser', 'pgadmin.backform', 'pgadmin.browser.server.privilege',
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
+  'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
+  'pgadmin.node.schema.dir/child', 'pgadmin.browser.server.privilege',
   'pgadmin.node.rule',
-], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform) {
+], function(
+  gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, schemaChild
+) {
+
 
   /**
     Create and add a view collection into nodes
@@ -28,14 +31,9 @@ define('pgadmin.node.view', [
     under which this node to display
     @param {variable} type - Type of Node
     @param {variable} hasSQL - To show SQL tab
-    @param {variable} canDrop - Adds drop view option
-    in the context menu
-    @param {variable} canDropCascade - Adds drop Cascade
-    view option in the context menu
    */
   if (!pgBrowser.Nodes['view']) {
-    pgBrowser.Nodes['view'] = pgBrowser.Node.extend({
-      parent_type: ['schema', 'catalog'],
+    pgBrowser.Nodes['view'] = schemaChild.SchemaChildNode.extend({
       type: 'view',
       sqlAlterHelp: 'sql-alterview.html',
       sqlCreateHelp: 'sql-createview.html',
@@ -45,8 +43,6 @@ define('pgadmin.node.view', [
       hasDepends: true,
       hasScriptTypes: ['create', 'select', 'insert'],
       collection_type: 'coll-view',
-      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
-      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
       Init: function() {
 
         // Avoid mulitple registration of menus
@@ -197,45 +193,6 @@ define('pgadmin.node.view', [
           return false;
         },
       }),
-
-      /**
-        Show or hide create view menu option on parent node
-        and hide for system view in catalogs.
-        */
-      canCreate: function(itemData, item, data) {
-
-        // If check is false then, we will allow create menu
-        if (data && data.check == false)
-          return true;
-
-        var t = pgBrowser.tree, i = item, d = itemData;
-
-        // To iterate over tree to check parent node
-        while (i) {
-
-          // If it is schema then allow user to create view
-          if (_.indexOf(['schema'], d._type) > -1)
-            return true;
-
-          if ('coll-view' == d._type) {
-
-            // Check if we are not child of view
-            var prev_i = t.hasParent(i) ? t.parent(i) : null,
-              prev_d = prev_i ? t.itemData(prev_i) : null;
-            if( prev_d._type == 'catalog') {
-              return false;
-            } else {
-              return true;
-            }
-          }
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-
-        // by default we do not want to allow create menu
-        return true;
-
-      },
     });
   }
 
diff --git a/web/pgadmin/browser/static/js/collection.js b/web/pgadmin/browser/static/js/collection.js
index 05f0edd5..67e44214 100644
--- a/web/pgadmin/browser/static/js/collection.js
+++ b/web/pgadmin/browser/static/js/collection.js
@@ -115,17 +115,17 @@ define([
         // Fetch Data
         collection.fetch({
           reset: true,
-          error: function(xhr, error, message) {
+          error: function(model, error, xhr) {
             pgBrowser.Events.trigger(
               'pgadmin:collection:retrieval:error', 'properties', xhr, error,
-              message, item, that
+              error.message, item, that
             );
             if (!Alertify.pgHandleItemError(
-              xhr, error, message, {item: item, info: info}
+              xhr, error, error.message, {item: item, info: info}
             )) {
               Alertify.pgNotifier(error, xhr, S(
                 gettext('Error retrieving properties - %s.')
-              ).sprintf(message || that.label).value(), function() {
+              ).sprintf(error.message || that.label).value(), function() {
                 console.warn(arguments);
               });
             }
diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js
index 460ab03d..35eca24b 100644
--- a/web/pgadmin/browser/static/js/node.js
+++ b/web/pgadmin/browser/static/js/node.js
@@ -1,9 +1,13 @@
 define('pgadmin.browser.node', [
+  'sources/tree/pgadmin_tree_node',
   'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin',
   'pgadmin.browser.menu', 'backbone', 'pgadmin.alertifyjs', 'pgadmin.browser.datamodel',
   'backform', 'sources/browser/generate_url', 'sources/utils', 'pgadmin.browser.utils',
   'pgadmin.backform',
-], function(gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl, commonUtils) {
+], function(
+  pgadminTreeNode,
+  gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl, commonUtils
+) {
 
   var wcDocker = window.wcDocker,
     keyCode = {
@@ -28,7 +32,7 @@ define('pgadmin.browser.node', [
   //
   // It is unlikely - we will instantiate an object for this class.
   // (Inspired by Backbone.extend function)
-  pgBrowser.Node.extend = function(props) {
+  pgBrowser.Node.extend = function(props, initialize) {
     var parent = this;
     var child;
 
@@ -44,6 +48,10 @@ define('pgadmin.browser.node', [
     // Make sure - a child have all the callbacks of the parent.
     child.callbacks = _.extend({}, parent.callbacks, props.callbacks);
 
+    // Let's not bind the callbacks, or initialize the child.
+    if (initialize === false)
+      return child;
+
     var bindToChild = function(cb) {
         if (typeof(child.callbacks[cb]) == 'function') {
           child.callbacks[cb] = child.callbacks[cb].bind(child);
@@ -1566,7 +1574,6 @@ define('pgadmin.browser.node', [
      * depends, statistics
      */
     generate_url: function(item, type, d, with_id, info) {
-
       var opURL = {
           'create': 'obj',
           'drop': 'obj',
@@ -1608,24 +1615,7 @@ define('pgadmin.browser.node', [
     Collection: pgBrowser.DataCollection,
     // Base class for Node Data Model
     Model: pgBrowser.DataModel,
-    getTreeNodeHierarchy: function(i) {
-      var idx = 0,
-        res = {},
-        t = pgBrowser.tree,
-        d;
-      do {
-        d = t.itemData(i);
-        if (d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId) {
-          res[d._type] = _.extend({}, d, {
-            'priority': idx,
-          });
-          idx -= 1;
-        }
-        i = t.hasParent(i) ? t.parent(i) : null;
-      } while (i);
-
-      return res;
-    },
+    getTreeNodeHierarchy: pgadminTreeNode.getTreeNodeHierarchyFromIdentifier.bind(pgBrowser),
     cache: function(url, node_info, level, data) {
       var cached = this.cached = this.cached || {},
         hash = url,
diff --git a/web/pgadmin/static/js/alertify/dialog.js b/web/pgadmin/static/js/alertify/dialog.js
new file mode 100644
index 00000000..4a0a1b89
--- /dev/null
+++ b/web/pgadmin/static/js/alertify/dialog.js
@@ -0,0 +1,150 @@
+/////////////////////////////////////////////////////////////
+//
+// 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);
+
+    if (treeNode) {
+      let nodeData;
+      let databaseNode = treeNode.ancestorNode(
+        (node) => {
+          nodeData = node.getData();
+          return (nodeData._type === 'database');
+        }
+      );
+      let isServerNode = (node) => {
+        nodeData = node.getData();
+        return nodeData._type === 'server';
+      };
+
+      if (databaseNode !== null) {
+        if (nodeData._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.'
+            )
+          );
+        } else {
+          if (databaseNode.hasParent(isServerNode))
+            serverInformation = nodeData;
+        }
+      } else {
+        if (treeNode.anyFamilyMember(isServerNode))
+          serverInformation = nodeData;
+      }
+    }
+
+    if (serverInformation === null) {
+      this.alertify.alert(
+        gettext(this.errorAlertTitle),
+        gettext('Please select server or child node from the browser tree.')
+      );
+    }
+
+    return serverInformation;
+  }
+
+  hasBinariesConfiguration(serverInformation) {
+    const module = 'paths';
+    let preference_name = 'pg_bin_dir';
+    let msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
+
+    if ((serverInformation.type && serverInformation.type === 'ppas') ||
+      serverInformation.server_type === 'ppas') {
+      preference_name = 'ppas_bin_dir';
+      msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
+    }
+    const preference = this.pgBrowser.get_preference(module, preference_name);
+
+    if (preference) {
+      if (!preference.value) {
+        this.alertify.alert(gettext('Configuration required'), msg);
+        return false;
+      }
+    } else {
+      this.alertify.alert(
+        gettext(this.errorAlertTitle),
+        sprintf(gettext('Failed to load preference %s of module %s'), preference_name, module)
+      );
+      return false;
+    }
+    return true;
+  }
+
+  dialogName() {
+    return undefined;
+  }
+
+  createOrGetDialog(dialogTitle, typeOfDialog) {
+    const dialogName = this.dialogName(typeOfDialog);
+
+    if (!this.alertify[dialogName]) {
+      const self = this;
+      this.alertify.dialog(dialogName, function factory() {
+        return self.dialogFactory(dialogTitle, typeOfDialog);
+      });
+    }
+    return this.alertify[dialogName];
+  }
+
+  dialogFactory(dialogTitle, typeOfDialog) {
+    const factory = new DialogFactory(
+      this.pgBrowser,
+      this.jquery,
+      this.alertify,
+      this.dialogModel,
+      this.backform,
+      this.dialogContainerSelector);
+    return factory.create(dialogTitle, typeOfDialog);
+  }
+
+  canExecuteOnCurrentDatabase(aciTreeItem) {
+    const treeInfo = getTreeNodeHierarchyFromIdentifier.apply(this.pgBrowser, [aciTreeItem]);
+
+    if (treeInfo.database && treeInfo.database._label.indexOf('=') >= 0) {
+      this.alertify.alert(
+        gettext(this.errorAlertTitle),
+        gettext('Databases with = symbols in the name cannot be backed up or restored using this utility.')
+      );
+      return false;
+    }
+
+    return true;
+  }
+}
diff --git a/web/pgadmin/static/js/alertify/dialog_factory.js b/web/pgadmin/static/js/alertify/dialog_factory.js
new file mode 100644
index 00000000..500140b8
--- /dev/null
+++ b/web/pgadmin/static/js/alertify/dialog_factory.js
@@ -0,0 +1,52 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import * as BackupDialog from '../../../tools/backup/static/js/backup_dialog_wrapper';
+import {RestoreDialogWrapper} from '../../../tools/restore/static/js/restore_dialog_wrapper';
+
+export class DialogFactory {
+  constructor(pgBrowser, $,
+              alertify, DialogModel,
+              backform, dialogContainerSelector) {
+    this.pgBrowser = pgBrowser;
+    this.jquery = $;
+    this.alertify = alertify;
+    this.dialogModel = DialogModel;
+    this.backform = backform;
+    this.dialogContainerSelector = dialogContainerSelector;
+  }
+
+  create(dialogTitle, typeOfDialog) {
+    if (typeOfDialog === 'restore') {
+      return this.createRestoreDialog(dialogTitle, typeOfDialog);
+    } else {
+      return this.createBackupDialog(dialogTitle, typeOfDialog);
+    }
+  }
+
+  createRestoreDialog(dialogTitle, typeOfDialog) {
+    return new RestoreDialogWrapper(
+      this.dialogContainerSelector, dialogTitle, typeOfDialog,
+      this.jquery,
+      this.pgBrowser,
+      this.alertify,
+      this.dialogModel,
+      this.backform);
+  }
+
+  createBackupDialog(dialogTitle, typeOfDialog) {
+    return new BackupDialog.BackupDialogWrapper(
+      this.dialogContainerSelector, dialogTitle, typeOfDialog,
+      this.jquery,
+      this.pgBrowser,
+      this.alertify,
+      this.dialogModel,
+      this.backform);
+  }
+}
diff --git a/web/pgadmin/static/js/alertify/dialog_wrapper.js b/web/pgadmin/static/js/alertify/dialog_wrapper.js
new file mode 100644
index 00000000..b5ff8204
--- /dev/null
+++ b/web/pgadmin/static/js/alertify/dialog_wrapper.js
@@ -0,0 +1,57 @@
+import * as commonUtils from '../utils';
+
+export class DialogWrapper {
+  constructor(
+    dialogContainerSelector, dialogTitle, jquery, pgBrowser,
+    alertify, dialogModel, backform) {
+    this.hooks = {
+      onclose: function () {
+        if (this.view) {
+          this.view.remove({
+            data: true,
+            internal: true,
+            silent: true,
+          });
+        }
+      },
+    };
+    this.dialogContainerSelector = dialogContainerSelector;
+    this.dialogTitle = dialogTitle;
+    this.jquery = jquery;
+    this.pgBrowser = pgBrowser;
+    this.alertify = alertify;
+    this.dialogModel = dialogModel;
+    this.backform = backform;
+  }
+
+  build() {
+    this.alertify.pgDialogBuild.apply(this);
+  }
+
+  wasHelpButtonPressed(e) {
+    return e.button.element.name === 'dialog_help'
+      || e.button.element.name === 'object_help';
+  }
+
+  getSelectedNodeData(selectedTreeNode) {
+    if (!this.isNodeSelected(selectedTreeNode)) {
+      return undefined;
+    }
+    const treeNodeData = selectedTreeNode.getData();
+    if (treeNodeData) {
+      return treeNodeData;
+    }
+    return undefined;
+  }
+
+  focusOnDialog(dialog) {
+    dialog.$el.attr('tabindex', -1);
+    this.pgBrowser.keyboardNavigation.getDialogTabNavigator(dialog);
+    const container = dialog.$el.find('.tab-content:first > .tab-pane.active:first');
+    commonUtils.findAndSetFocus(container);
+  }
+
+  isNodeSelected(selectedTreeNode) {
+    return selectedTreeNode;
+  }
+}
diff --git a/web/pgadmin/static/js/nodes/supported_database_node.js b/web/pgadmin/static/js/nodes/supported_database_node.js
new file mode 100644
index 00000000..fde1cf98
--- /dev/null
+++ b/web/pgadmin/static/js/nodes/supported_database_node.js
@@ -0,0 +1,37 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {isValidTreeNodeData} from 'sources/tree/tree';
+
+function checkAllowConnIfDatabaseNode(treeNodeData) {
+  return (treeNodeData._type === 'database' && treeNodeData.allowConn)
+    || treeNodeData._type !== 'database';
+}
+
+function ancestorWithTypeCatalog(treeNode) {
+  return treeNode.anyFamilyMember((node) => {
+    return node.getData()._type === 'catalog';
+  });
+}
+
+export function enabled(tree, supportedNodes, treeNodeData, domTreeNode) {
+  if (!isValidTreeNodeData(treeNodeData))
+    return false;
+
+  let treeNode = tree.findNodeByDomElement(domTreeNode);
+  if (!treeNode) {
+    return false;
+  }
+
+  return checkAllowConnIfDatabaseNode(treeNodeData) &&
+    _.indexOf(supportedNodes, treeNodeData._type) !== -1 &&
+    !ancestorWithTypeCatalog(treeNode);
+}
+
+
diff --git a/web/pgadmin/static/js/tree/pgadmin_tree_node.js b/web/pgadmin/static/js/tree/pgadmin_tree_node.js
new file mode 100644
index 00000000..00f10d24
--- /dev/null
+++ b/web/pgadmin/static/js/tree/pgadmin_tree_node.js
@@ -0,0 +1,72 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+/**
+ * This method received pgBrowser and new TreeNode object
+ *
+ * This method retrieves all the data that exists in the tree node and in
+ * `pgBrowser.Nodes` for all the parent node of the provided node.
+ *
+ * The 2 condition to get the information from pgBrowser.Nodes are:
+ *  1 - the variable _type of the tree node
+ *  2 - the presence of hasId in the pgBrowser.Nodes for the specific node
+ *
+ * Number 2 is used to ignore coll-* nodes as they do not add any useful
+ * information
+ */
+export function getTreeNodeHierarchyFromElement(pgBrowser, treeNode) {
+  return getTreeNodeHierarchy.call(pgBrowser, treeNode);
+}
+
+/**
+ * This method received an ACI Tree JQuery node
+ *
+ * NOTE: this function need to be called on pgBrowser instance.
+ * getTreeNodeHierarchyFromIdentifier.apply(pgBrowser, [aciTreeNodeIdentifier])
+ *
+ * This method retrieves all the data that exists in the tree node and in
+ * `pgBrowser.Nodes` for all the parent node of the provided node.
+ *
+ * The 2 condition to get the information from pgBrowser.Nodes are:
+ *  1 - the variable _type of the tree node
+ *  2 - the presence of hasId in the pgBrowser.Nodes for the specific node
+ *
+ * Number 2 is used to ignore coll-* nodes as they do not add any useful
+ * information
+ */
+export function getTreeNodeHierarchyFromIdentifier(aciTreeNodeIdentifier) {
+  let identifier = this.treeMenu.translateTreeNodeIdFromACITree(aciTreeNodeIdentifier);
+  let currentNode = this.treeMenu.findNode(identifier);
+  return getTreeNodeHierarchy.call(this, currentNode);
+}
+
+export function getTreeNodeHierarchy(currentNode) {
+  let idx = 0;
+  let result = {};
+
+  do {
+    const currentNodeData = currentNode.getData();
+    if (currentNodeData._type in this.Nodes && this.Nodes[currentNodeData._type].hasId) {
+      const nodeType = mapType(currentNodeData._type);
+      if (result[nodeType] === undefined) {
+        result[nodeType] = _.extend({}, currentNodeData, {
+          'priority': idx,
+        });
+        idx -= 1;
+      }
+    }
+    currentNode = currentNode.hasParent() ? currentNode.parent() : null;
+  } while (currentNode);
+
+  return result;
+}
+
+function mapType(type) {
+  return type === 'partition' ? 'table' : type;
+}
diff --git a/web/pgadmin/static/js/tree/tree.js b/web/pgadmin/static/js/tree/tree.js
index 01edb6c3..b5655a70 100644
--- a/web/pgadmin/static/js/tree/tree.js
+++ b/web/pgadmin/static/js/tree/tree.js
@@ -59,17 +59,20 @@ export class TreeNode {
     tree.aciTreeApi.unload(this.domNode);
   }
 
-  anyParent(condition) {
+  /*
+   * Find the ancestor with matches this condition
+   */
+  ancestorNode(condition) {
     let node = this;
 
     while (node.hasParent()) {
       node = node.parent();
       if (condition(node)) {
-        return true;
+        return node;
       }
     }
 
-    return false;
+    return null;
   }
 
   /**
@@ -81,7 +84,10 @@ export class TreeNode {
       return true;
     }
 
-    return this.anyParent(condition);
+    return this.ancestorNode(condition) !== null;
+  }
+  anyParent(condition) {
+    return this.ancestorNode(condition) !== null;
   }
 }
 
@@ -210,3 +216,7 @@ function findInTree(rootNode, path) {
     }
   })(rootNode);
 }
+
+export function isValidTreeNodeData(treeNodeData) {
+  return !_.isUndefined(treeNodeData) && !_.isNull(treeNodeData);
+}
diff --git a/web/pgadmin/tools/backup/static/js/backup.js b/web/pgadmin/tools/backup/static/js/backup.js
index 94ab8b76..d6fd48c5 100644
--- a/web/pgadmin/tools/backup/static/js/backup.js
+++ b/web/pgadmin/tools/backup/static/js/backup.js
@@ -3,9 +3,12 @@ define([
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'underscore.string', 'pgadmin.alertifyjs', 'backbone', 'pgadmin.backgrid',
   'pgadmin.backform', 'pgadmin.browser', 'sources/utils',
+  'tools/backup/static/js/menu_utils',
+  'tools/backup/static/js/backup_dialog',
+  'sources/nodes/supported_database_node',
 ], function(
   gettext, url_for, $, _, S, alertify, Backbone, Backgrid, Backform, pgBrowser,
-commonUtils
+  commonUtils, menuUtils, globalBackupDialog, supportedNodes
 ) {
 
   // if module is already initialized, refer to that.
@@ -394,48 +397,6 @@ commonUtils
 
       this.initialized = true;
 
-      // Define list of nodes on which backup context menu option appears
-      var backup_supported_nodes = [
-        'database', 'schema', 'table', 'partition',
-      ];
-
-      /**
-        Enable/disable backup menu in tools based
-        on node selected
-        if selected node is present in supported_nodes,
-        menu will be enabled otherwise disabled.
-        Also, hide it for system view in catalogs
-      */
-      var menu_enabled = function(itemData, item) {
-        var t = pgBrowser.tree,
-          i = item,
-          d = itemData,
-          parent_item = t.hasParent(i) ? t.parent(i) : null,
-          parent_data = parent_item ? t.itemData(parent_item) : null;
-
-        if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
-          if (_.indexOf(backup_supported_nodes, d._type) !== -1 &&
-            parent_data._type != 'catalog') {
-            if (d._type == 'database' && d.allowConn)
-              return true;
-            else if (d._type != 'database')
-              return true;
-            else
-              return false;
-          } else
-            return false;
-        } else
-          return false;
-      };
-
-      var menu_enabled_server = function(itemData) {
-        // If server node selected && connected
-        if (!_.isUndefined(itemData) && !_.isNull(itemData))
-          return (('server' === itemData._type) && itemData.connected);
-        else
-          return false;
-      };
-
       // Define the nodes on which the menus to be appear
       var menus = [{
         name: 'backup_global',
@@ -445,7 +406,7 @@ commonUtils
         priority: 12,
         label: gettext('Backup Globals...'),
         icon: 'fa fa-floppy-o',
-        enable: menu_enabled_server,
+        enable: menuUtils.menuEnabledServer,
       }, {
         name: 'backup_server',
         module: this,
@@ -454,7 +415,7 @@ commonUtils
         priority: 12,
         label: gettext('Backup Server...'),
         icon: 'fa fa-floppy-o',
-        enable: menu_enabled_server,
+        enable: menuUtils.menuEnabledServer,
       }, {
         name: 'backup_global_ctx',
         module: this,
@@ -464,7 +425,7 @@ commonUtils
         priority: 12,
         label: gettext('Backup Globals...'),
         icon: 'fa fa-floppy-o',
-        enable: menu_enabled_server,
+        enable: menuUtils.menuEnabledServer,
       }, {
         name: 'backup_server_ctx',
         module: this,
@@ -474,7 +435,7 @@ commonUtils
         priority: 12,
         label: gettext('Backup Server...'),
         icon: 'fa fa-floppy-o',
-        enable: menu_enabled_server,
+        enable: menuUtils.menuEnabledServer,
       }, {
         name: 'backup_object',
         module: this,
@@ -483,20 +444,24 @@ commonUtils
         priority: 11,
         label: gettext('Backup...'),
         icon: 'fa fa-floppy-o',
-        enable: menu_enabled,
+        enable: supportedNodes.enabled.bind(
+          null, pgBrowser.treeMenu, menuUtils.backupSupportedNodes
+        ),
       }];
 
-      for (var idx = 0; idx < backup_supported_nodes.length; idx++) {
+      for (var idx = 0; idx < menuUtils.backupSupportedNodes.length; idx++) {
         menus.push({
-          name: 'backup_' + backup_supported_nodes[idx],
-          node: backup_supported_nodes[idx],
+          name: 'backup_' + menuUtils.backupSupportedNodes[idx],
+          node: menuUtils.backupSupportedNodes[idx],
           module: this,
           applies: ['context'],
           callback: 'backup_objects',
           priority: 11,
           label: gettext('Backup...'),
           icon: 'fa fa-floppy-o',
-          enable: menu_enabled,
+          enable: supportedNodes.enabled.bind(
+            null, pgBrowser.treeMenu, menuUtils.backupSupportedNodes
+          ),
         });
       }
 
@@ -521,542 +486,25 @@ commonUtils
     },
 
     // Callback to draw Backup Dialog for globals/server
-    start_backup_global_server: function(action, item, params) {
-      var i = item || pgBrowser.tree.selected(),
-        server_data = null;
-
-      while (i) {
-        var node_data = pgBrowser.tree.itemData(i);
-        if (node_data._type == 'server') {
-          server_data = node_data;
-          break;
-        }
-
-        if (pgBrowser.tree.hasParent(i)) {
-          i = $(pgBrowser.tree.parent(i));
-        } else {
-          alertify.alert(
-            gettext('Backup Error'),
-            gettext('Please select server or child node from the browser tree.')
-          );
-          break;
-        }
-      }
-
-      if (!server_data) {
-        return;
-      }
-
-      var module = 'paths',
-        preference_name = 'pg_bin_dir',
-        msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
-
-      if ((server_data.type && server_data.type == 'ppas') ||
-        server_data.server_type == 'ppas') {
-        preference_name = 'ppas_bin_dir';
-        msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
-      }
-
-      var preference = pgBrowser.get_preference(module, preference_name);
-
-      if (preference) {
-        if (!preference.value) {
-          alertify.alert(gettext('Configuration required'), msg);
-          return;
-        }
-      } else {
-        alertify.alert(
-          gettext('Backup Error'),
-          S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value()
-        );
-        return;
-      }
-
-      var of_type = undefined;
-
-      // Set Notes according to type of backup
-      if (!_.isUndefined(params['globals']) && params['globals']) {
-        of_type = 'globals';
-      } else {
-        of_type = 'server';
-      }
-
-      var DialogName = 'BackupDialog_' + of_type,
-        DialogTitle = ((of_type == 'globals') ?
-          gettext('Backup Globals...') :
-          gettext('Backup Server...'));
-
-      if (!alertify[DialogName]) {
-        alertify.dialog(DialogName, function factory() {
-          return {
-            main: function(title) {
-              this.set('title', title);
-            },
-            build: function() {
-              alertify.pgDialogBuild.apply(this);
-            },
-            setup: function() {
-              return {
-                buttons: [{
-                  text: '',
-                  className: 'btn btn-default pull-left fa fa-lg fa-info',
-                  attrs: {
-                    name: 'object_help',
-                    type: 'button',
-                    url: 'backup.html',
-                    label: gettext('Backup'),
-                  },
-                }, {
-                  text: '',
-                  key: 112,
-                  className: 'btn btn-default pull-left fa fa-lg fa-question',
-                  attrs: {
-                    name: 'dialog_help',
-                    type: 'button',
-                    label: gettext('Backup'),
-                    url: url_for('help.static', {
-                      'filename': 'backup_dialog.html',
-                    }),
-                  },
-                }, {
-                  text: gettext('Backup'),
-                  key: 13,
-                  className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
-                  'data-btn-name': 'backup',
-                }, {
-                  text: gettext('Cancel'),
-                  key: 27,
-                  className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
-                  'data-btn-name': 'cancel',
-                }],
-                // Set options for dialog
-                options: {
-                  title: DialogTitle,
-                  //disable both padding and overflow control.
-                  padding: !1,
-                  overflow: !1,
-                  model: 0,
-                  resizable: true,
-                  maximizable: true,
-                  pinnable: false,
-                  closableByDimmer: false,
-                  modal: false,
-                },
-              };
-            },
-            hooks: {
-              // Triggered when the dialog is closed
-              onclose: function() {
-                if (this.view) {
-                  // clear our backform model/view
-                  this.view.remove({
-                    data: true,
-                    internal: true,
-                    silent: true,
-                  });
-                }
-              },
-            },
-            prepare: function() {
-              var self = this;
-              // Disable Backup button until user provides Filename
-              this.__internal.buttons[2].element.disabled = true;
-
-              var $container = $('<div class=\'backup_dialog\'></div>');
-              // Find current/selected node
-              var t = pgBrowser.tree,
-                i = t.selected(),
-                d = i && i.length == 1 ? t.itemData(i) : undefined,
-                node = d && pgBrowser.Nodes[d._type];
-
-              if (!d)
-                return;
-              // Create treeInfo
-              var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
-              // Instance of backbone model
-              var newModel = new BackupModel({
-                  type: of_type,
-                }, {
-                  node_info: treeInfo,
-                }),
-                fields = Backform.generateViewSchema(
-                  treeInfo, newModel, 'create', node, treeInfo.server, true
-                );
-
-              var view = this.view = new Backform.Dialog({
-                el: $container,
-                model: newModel,
-                schema: fields,
-              });
-              // Add our class to alertify
-              $(this.elements.body.childNodes[0]).addClass(
-                'alertify_tools_dialog_properties obj_properties'
-              );
-              // Render dialog
-              view.render();
-
-              this.elements.content.appendChild($container.get(0));
-
-              var container = view.$el.find('.tab-content:first > .tab-pane.active:first');
-              commonUtils.findAndSetFocus(container);
-
-              // Listen to model & if filename is provided then enable Backup button
-              this.view.model.on('change', function() {
-                if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
-                  this.errorModel.clear();
-                  self.__internal.buttons[2].element.disabled = false;
-                } else {
-                  self.__internal.buttons[2].element.disabled = true;
-                  this.errorModel.set('file', gettext('Please provide a filename'));
-                }
-              });
-            },
-            // Callback functions when click on the buttons of the Alertify dialogs
-            callback: function(e) {
-              // Fetch current server id
-              var t = pgBrowser.tree,
-                i = t.selected(),
-                d = i && i.length == 1 ? t.itemData(i) : undefined,
-                node = d && pgBrowser.Nodes[d._type];
-
-              if (e.button.element.name == 'dialog_help' || e.button.element.name == 'object_help') {
-                e.cancel = true;
-                pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
-                  node, i, e.button.element.getAttribute('label'));
-                return;
-              }
-
-              if (e.button['data-btn-name'] === 'backup') {
-
-                if (!d)
-                  return;
-
-                var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
-
-                var self = this,
-                  baseUrl = url_for('backup.create_server_job', {
-                    'sid': treeInfo.server._id,
-                  }),
-                  args = this.view.model.toJSON();
-
-                $.ajax({
-                  url: baseUrl,
-                  method: 'POST',
-                  data: {
-                    'data': JSON.stringify(args),
-                  },
-                  success: function(res) {
-                    if (res.success) {
-                      alertify.success(gettext('Backup job created.'), 5);
-                      pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
-                    } else {
-                      console.warn(res);
-                    }
-                  },
-                  error: function(xhr) {
-                    try {
-                      var err = JSON.parse(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 = JSON.parse(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..2f530d80
--- /dev/null
+++ b/web/pgadmin/tools/backup/static/js/backup_dialog.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
+//
+//////////////////////////////////////////////////////////////
+
+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 42122ed8..c730b180 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 5ce0e2fb..2ace07e5 100644
--- a/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js
+++ b/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js
@@ -2,12 +2,15 @@
 define([
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
   'pgadmin.alertifyjs', 'pgadmin.backgrid', 'pgadmin.backform',
-  'pgadmin.browser', 'pgadmin.browser.node', 'backgrid.select.all',
+  'pgadmin.browser', 'pgadmin.browser.node',
+  'tools/grant_wizard/static/js/menu_utils',
+  'sources/nodes/supported_database_node',
+  'backgrid.select.all',
   'backgrid.filter', 'pgadmin.browser.server.privilege',
   'pgadmin.browser.wizard',
 ], function(
   gettext, url_for, $, _, Backbone, Alertify, Backgrid, Backform, pgBrowser,
-  pgNode
+  pgNode, menuUtils, supportedNodes
 ) {
 
   // if module is already initialized, refer to that.
@@ -143,41 +146,6 @@ define([
 
       this.initialized = true;
 
-      // Define list of nodes on which grant wizard context menu option appears
-      var supported_nodes = [
-          'schema', 'coll-function', 'coll-sequence',
-          'coll-table', 'coll-view', 'coll-procedure',
-          'coll-mview', 'database', 'coll-trigger_function',
-        ],
-
-        /**
-          Enable/disable grantwizard menu in tools based
-          on node selected
-          if selected node is present in supported_nodes,
-          menu will be enabled otherwise disabled.
-          Also, hide it for system view in catalogs
-        */
-        menu_enabled = function(itemData, item) {
-          var t = pgBrowser.tree,
-            i = item,
-            d = itemData;
-          var parent_item = t.hasParent(i) ? t.parent(i) : null,
-            parent_data = parent_item ? t.itemData(parent_item) : null;
-          if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
-            if (_.indexOf(supported_nodes, d._type) !== -1 &&
-              parent_data._type != 'catalog') {
-              if (d._type == 'database' && d.allowConn)
-                return true;
-              else if (d._type != 'database')
-                return true;
-              else
-                return false;
-            } else
-              return false;
-          } else
-            return false;
-        };
-
       // Define the nodes on which the menus to be appear
       var menus = [{
         name: 'grant_wizard_schema',
@@ -187,21 +155,25 @@ define([
         priority: 14,
         label: gettext('Grant Wizard...'),
         icon: 'fa fa-unlock-alt',
-        enable: menu_enabled,
+        enable: supportedNodes.enabled.bind(
+          null, pgBrowser.treeMenu, menuUtils.supportedNodes
+        ),
       }];
 
       // Add supported menus into the menus list
-      for (var idx = 0; idx < supported_nodes.length; idx++) {
+      for (var idx = 0; idx < menuUtils.supportedNodes.length; idx++) {
         menus.push({
-          name: 'grant_wizard_schema_context_' + supported_nodes[idx],
-          node: supported_nodes[idx],
+          name: 'grant_wizard_schema_context_' + menuUtils.supportedNodes[idx],
+          node: menuUtils.supportedNodes[idx],
           module: this,
           applies: ['context'],
           callback: 'start_grant_wizard',
           priority: 14,
           label: gettext('Grant Wizard...'),
           icon: 'fa fa-unlock-alt',
-          enable: menu_enabled,
+          enable: supportedNodes.enabled.bind(
+            null, pgBrowser.treeMenu, menuUtils.supportedNodes
+          ),
         });
       }
       pgBrowser.add_menus(menus);
diff --git a/web/pgadmin/tools/grant_wizard/static/js/menu_utils.js b/web/pgadmin/tools/grant_wizard/static/js/menu_utils.js
new file mode 100644
index 00000000..b56ce3cd
--- /dev/null
+++ b/web/pgadmin/tools/grant_wizard/static/js/menu_utils.js
@@ -0,0 +1,16 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+export const supportedNodes = [
+  'schema', 'coll-function', 'coll-sequence',
+  'coll-table', 'coll-view', 'coll-procedure',
+  'coll-mview', 'database', 'coll-trigger_function',
+];
+
+
diff --git a/web/pgadmin/tools/import_export/static/js/import_export.js b/web/pgadmin/tools/import_export/static/js/import_export.js
index e64d3dee..5c73537e 100644
--- a/web/pgadmin/tools/import_export/static/js/import_export.js
+++ b/web/pgadmin/tools/import_export/static/js/import_export.js
@@ -1,10 +1,12 @@
 define([
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'pgadmin.alertifyjs',
   'sources/pgadmin', 'pgadmin.browser', 'backbone', 'backgrid', 'backform',
-  'sources/utils', 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui',
+  'sources/utils',
+  'sources/nodes/supported_database_node',
+  'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui',
 ], function(
   gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
-Backform, commonUtils
+Backform, commonUtils, supportedNodes
 ) {
 
   pgAdmin = pgAdmin || window.pgAdmin || {};
@@ -383,25 +385,6 @@ Backform, commonUtils
 
       this.initialized = true;
 
-      /*
-       * Enable/disable import menu in tools based on node selected. Import
-       * menu will be enabled only when user select table node.
-       */
-      var menu_enabled = function(itemData, item) {
-        var t = pgBrowser.tree,
-          i = item,
-          d = itemData;
-        var parent_item = t.hasParent(i) ? t.parent(i) : null,
-          parent_data = parent_item ? t.itemData(parent_item) : null;
-        if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data))
-          return (
-            (_.indexOf(['table'], d._type) !== -1 &&
-              parent_data._type != 'catalog') ? true : false
-          );
-        else
-          return false;
-      };
-
       // Initialize the context menu to display the import options when user open the context menu for table
       pgBrowser.add_menus([{
         name: 'import',
@@ -413,7 +396,9 @@ Backform, commonUtils
         priority: 10,
         label: gettext('Import/Export...'),
         icon: 'fa fa-shopping-cart',
-        enable: menu_enabled,
+        enable: supportedNodes.enabled.bind(
+          null, pgBrowser.treeMenu, ['table']
+        ),
       }]);
     },
 
diff --git a/web/pgadmin/tools/maintenance/static/js/maintenance.js b/web/pgadmin/tools/maintenance/static/js/maintenance.js
index f2102602..df05c3d5 100644
--- a/web/pgadmin/tools/maintenance/static/js/maintenance.js
+++ b/web/pgadmin/tools/maintenance/static/js/maintenance.js
@@ -2,11 +2,14 @@ define([
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'underscore.string', 'pgadmin.alertifyjs', 'sources/pgadmin', 'pgadmin.browser', 'backbone',
   'backgrid', 'backform', 'sources/utils',
+  'tools/maintenance/static/js/menu_utils',
+  'sources/nodes/supported_database_node',
   'pgadmin.backform', 'pgadmin.backgrid',
   'pgadmin.browser.node.ui',
 ], function(
   gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
-  Backform, commonUtils
+  Backform, commonUtils,
+  menuUtils, supportedNodes
 ) {
 
   pgAdmin = pgAdmin || window.pgAdmin || {};
@@ -168,36 +171,6 @@ define([
 
       this.initialized = true;
 
-      var maintenance_supported_nodes = [
-        'database', 'table', 'primary_key',
-        'unique_constraint', 'index', 'partition',
-      ];
-
-      /**
-       Enable/disable Maintenance menu in tools based on node selected.
-       Maintenance menu will be enabled only when user select table and database node.
-      */
-      var menu_enabled = function(itemData, item) {
-        var t = pgBrowser.tree,
-          i = item,
-          d = itemData;
-        var parent_item = t.hasParent(i) ? t.parent(i) : null,
-          parent_data = parent_item ? t.itemData(parent_item) : null;
-        if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
-          if (_.indexOf(maintenance_supported_nodes, d._type) !== -1 &&
-            parent_data._type != 'catalog') {
-            if (d._type == 'database' && d.allowConn)
-              return true;
-            else if (d._type != 'database')
-              return true;
-            else
-              return false;
-          } else
-            return false;
-        } else
-          return false;
-      };
-
       var menus = [{
         name: 'maintenance',
         module: this,
@@ -206,21 +179,25 @@ define([
         priority: 10,
         label: gettext('Maintenance...'),
         icon: 'fa fa-wrench',
-        enable: menu_enabled,
+        enable: supportedNodes.enabled.bind(
+          null, pgBrowser.treeMenu, menuUtils.maintenanceSupportedNodes
+        ),
       }];
 
       // Add supported menus into the menus list
-      for (var idx = 0; idx < maintenance_supported_nodes.length; idx++) {
+      for (var idx = 0; idx < menuUtils.maintenanceSupportedNodes.length; idx++) {
         menus.push({
-          name: 'maintenance_context_' + maintenance_supported_nodes[idx],
-          node: maintenance_supported_nodes[idx],
+          name: 'maintenance_context_' + menuUtils.maintenanceSupportedNodes[idx],
+          node: menuUtils.maintenanceSupportedNodes[idx],
           module: this,
           applies: ['context'],
           callback: 'callback_maintenance',
           priority: 10,
           label: gettext('Maintenance...'),
           icon: 'fa fa-wrench',
-          enable: menu_enabled,
+          enable: supportedNodes.enabled.bind(
+            null, pgBrowser.treeMenu, menuUtils.maintenanceSupportedNodes
+          ),
         });
       }
       pgBrowser.add_menus(menus);
diff --git a/web/pgadmin/tools/maintenance/static/js/menu_utils.js b/web/pgadmin/tools/maintenance/static/js/menu_utils.js
new file mode 100644
index 00000000..8cde1baa
--- /dev/null
+++ b/web/pgadmin/tools/maintenance/static/js/menu_utils.js
@@ -0,0 +1,13 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+export const maintenanceSupportedNodes = [
+  'database', 'table', 'primary_key',
+  'unique_constraint', 'index', 'partition',
+];
diff --git a/web/pgadmin/tools/restore/static/js/menu_utils.js b/web/pgadmin/tools/restore/static/js/menu_utils.js
new file mode 100644
index 00000000..2d35c951
--- /dev/null
+++ b/web/pgadmin/tools/restore/static/js/menu_utils.js
@@ -0,0 +1,18 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+export const restoreSupportedNodes = [
+  'database',
+  'schema',
+  'table',
+  'function',
+  'trigger',
+  'index',
+  'partition',
+];
diff --git a/web/pgadmin/tools/restore/static/js/restore.js b/web/pgadmin/tools/restore/static/js/restore.js
index 7daa7645..aeff6d3d 100644
--- a/web/pgadmin/tools/restore/static/js/restore.js
+++ b/web/pgadmin/tools/restore/static/js/restore.js
@@ -1,11 +1,13 @@
-// Restore dialog
 define('tools.restore', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
   'underscore.string', 'pgadmin.alertifyjs', 'pgadmin.browser',
   'pgadmin.backgrid', 'pgadmin.backform', 'sources/utils',
+  'tools/restore/static/js/menu_utils',
+  'sources/nodes/supported_database_node',
+  'tools/restore/static/js/restore_dialog',
 ], function(
   gettext, url_for, $, _, Backbone, S, alertify, pgBrowser, Backgrid, Backform,
-commonUtils
+commonUtils, menuUtils, supportedNodes, restoreDialog
 ) {
 
   // if module is already initialized, refer to that.
@@ -307,59 +309,6 @@ commonUtils
 
       this.initialized = true;
 
-      // Define list of nodes on which restore context menu option appears
-      var restore_supported_nodes = [
-        'database', 'schema',
-        'table', 'function',
-        'trigger', 'index',
-        'partition',
-      ];
-
-      /**
-        Enable/disable restore menu in tools based
-        on node selected
-        if selected node is present in supported_nodes,
-        menu will be enabled otherwise disabled.
-        Also, hide it for system view in catalogs
-      */
-      var menu_enabled = function(itemData, item, data) {
-        var t = pgBrowser.tree,
-          i = item,
-          d = itemData;
-        var parent_item = t.hasParent(i) ? t.parent(i) : null,
-          parent_data = parent_item ? t.itemData(parent_item) : null;
-        if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
-          if (_.indexOf(restore_supported_nodes, d._type) !== -1 &&
-            is_parent_catalog(itemData, item, data)) {
-            if (d._type == 'database' && d.allowConn)
-              return true;
-            else if (d._type != 'database')
-              return true;
-            else
-              return false;
-          } else
-            return false;
-        } else
-          return false;
-      };
-
-      var is_parent_catalog = function(itemData, item) {
-        var t = pgBrowser.tree,
-          i = item,
-          d = itemData;
-
-        // To iterate over tree to check parent node
-        while (i) {
-          // If it is schema then allow user to restore
-          if (_.indexOf(['catalog'], d._type) > -1)
-            return false;
-          i = t.hasParent(i) ? t.parent(i) : null;
-          d = i ? t.itemData(i) : null;
-        }
-        // by default we do not want to allow create menu
-        return true;
-      };
-
       // Define the nodes on which the menus to be appear
       var menus = [{
         name: 'restore_object',
@@ -369,20 +318,24 @@ commonUtils
         priority: 13,
         label: gettext('Restore...'),
         icon: 'fa fa-upload',
-        enable: menu_enabled,
+        enable: supportedNodes.enabled.bind(
+          null, pgBrowser.treeMenu, menuUtils.restoreSupportedNodes
+        ),
       }];
 
-      for (var idx = 0; idx < restore_supported_nodes.length; idx++) {
+      for (var idx = 0; idx < menuUtils.restoreSupportedNodes.length; idx++) {
         menus.push({
-          name: 'restore_' + restore_supported_nodes[idx],
-          node: restore_supported_nodes[idx],
+          name: 'restore_' + menuUtils.restoreSupportedNodes[idx],
+          node: menuUtils.restoreSupportedNodes[idx],
           module: this,
           applies: ['context'],
           callback: 'restore_objects',
           priority: 13,
           label: gettext('Restore...'),
           icon: 'fa fa-upload',
-          enable: menu_enabled,
+          enable: supportedNodes.enabled.bind(
+            null, pgBrowser.treeMenu, menuUtils.restoreSupportedNodes
+          ),
         });
       }
 
@@ -391,318 +344,10 @@ 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 = JSON.parse(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.js.rej b/web/pgadmin/tools/restore/static/js/restore.js.rej
new file mode 100644
index 00000000..43577f42
--- /dev/null
+++ b/web/pgadmin/tools/restore/static/js/restore.js.rej
@@ -0,0 +1,322 @@
+diff a/web/pgadmin/tools/restore/static/js/restore.js b/web/pgadmin/tools/restore/static/js/restore.js	(rejected hunks)
+@@ -391,318 +344,8 @@ commonUtils
+     },
+     // Callback to draw Backup Dialog for objects
+     restore_objects: function(action, treeItem) {
+-
+-      var i = treeItem || pgBrowser.tree.selected(),
+-        server_data = null;
+-
+-      while (i) {
+-        var node_data = pgBrowser.tree.itemData(i);
+-        if (node_data._type == 'server') {
+-          server_data = node_data;
+-          break;
+-        }
+-
+-        if (pgBrowser.tree.hasParent(i)) {
+-          i = $(pgBrowser.tree.parent(i));
+-        } else {
+-          alertify.alert(
+-            gettext('Restore Error'),
+-            gettext('Please select server or child node from tree.')
+-          );
+-          break;
+-        }
+-      }
+-
+-      if (!server_data) {
+-        return;
+-      }
+-
+-      var module = 'paths',
+-        preference_name = 'pg_bin_dir',
+-        msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
+-
+-      if ((server_data.type && server_data.type == 'ppas') ||
+-        server_data.server_type == 'ppas') {
+-        preference_name = 'ppas_bin_dir';
+-        msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
+-      }
+-
+-      var preference = pgBrowser.get_preference(module, preference_name);
+-
+-      if (preference) {
+-        if (!preference.value) {
+-          alertify.alert(gettext('Configuration required'), msg);
+-          return;
+-        }
+-      } else {
+-        alertify.alert(
+-          gettext('Restore Error'),
+-          S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value()
+-        );
+-        return;
+-      }
+-
+-      var title = S(gettext('Restore (%s: %s)')),
+-        tree = pgBrowser.tree,
+-        item = treeItem || tree.selected(),
+-        data = item && item.length == 1 && tree.itemData(item),
+-        node = data && data._type && pgBrowser.Nodes[data._type];
+-
+-      if (!node)
+-        return;
+-
+-      var treeInfo = node.getTreeNodeHierarchy.apply(node, [item]);
+-
+-      if (treeInfo.database._label.indexOf('=') >= 0) {
+-        alertify.alert(
+-          gettext('Restore error'),
+-          gettext('Restore job creation failed. '+
+-          'Databases with = symbols in the name cannot be restored using this utility.')
+-        );
+-        return;
+-      }
+-
+-      title = title.sprintf(node.label, data.label).value();
+-
+-      if (!alertify.pg_restore) {
+-        // Create Dialog title on the fly with node details
+-        alertify.dialog('pg_restore', function factory() {
+-          return {
+-            main: function(title, item, data, node) {
+-              this.set('title', title);
+-              this.setting('pg_node', node);
+-              this.setting('pg_item', item);
+-              this.setting('pg_item_data', data);
+-            },
+-            build: function() {
+-              alertify.pgDialogBuild.apply(this);
+-            },
+-            setup: function() {
+-              return {
+-                buttons: [{
+-                  text: '',
+-                  className: 'btn btn-default pull-left fa fa-lg fa-info',
+-                  attrs: {
+-                    name: 'object_help',
+-                    type: 'button',
+-                    url: 'backup.html',
+-                    label: gettext('Restore'),
+-                  },
+-                }, {
+-                  text: '',
+-                  key: 112,
+-                  className: 'btn btn-default pull-left fa fa-lg fa-question',
+-                  attrs: {
+-                    name: 'dialog_help',
+-                    type: 'button',
+-                    label: gettext('Restore'),
+-                    url: url_for('help.static', {
+-                      'filename': 'restore_dialog.html',
+-                    }),
+-                  },
+-                }, {
+-                  text: gettext('Restore'),
+-                  key: 13,
+-                  className: 'btn btn-primary fa fa-upload pg-alertify-button',
+-                  restore: true,
+-                  'data-btn-name': 'restore',
+-                }, {
+-                  text: gettext('Cancel'),
+-                  key: 27,
+-                  className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+-                  restore: false,
+-                  'data-btn-name': 'cancel',
+-                }],
+-                // Set options for dialog
+-                options: {
+-                  title: title,
+-                  //disable both padding and overflow control.
+-                  padding: !1,
+-                  overflow: !1,
+-                  model: 0,
+-                  resizable: true,
+-                  maximizable: true,
+-                  pinnable: false,
+-                  closableByDimmer: false,
+-                  modal: false,
+-                },
+-              };
+-            },
+-            hooks: {
+-              // triggered when the dialog is closed
+-              onclose: function() {
+-                if (this.view) {
+-                  this.view.remove({
+-                    data: true,
+-                    internal: true,
+-                    silent: true,
+-                  });
+-                }
+-              },
+-            },
+-            settings: {
+-              pg_node: null,
+-              pg_item: null,
+-              pg_item_data: null,
+-            },
+-            prepare: function() {
+-
+-              var self = this;
+-              // Disable Backup button until user provides Filename
+-              this.__internal.buttons[2].element.disabled = true;
+-              var $container = $('<div class=\'restore_dialog\'></div>');
+-              var t = pgBrowser.tree,
+-                i = t.selected(),
+-                d = i && i.length == 1 ? t.itemData(i) : undefined,
+-                node = d && pgBrowser.Nodes[d._type];
+-
+-              if (!d)
+-                return;
+-
+-              var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+-
+-              var newModel = new RestoreObjectModel({
+-                  node_data: node,
+-                }, {
+-                  node_info: treeInfo,
+-                }),
+-                fields = Backform.generateViewSchema(
+-                  treeInfo, newModel, 'create', node, treeInfo.server, true
+-                );
+-
+-              var view = this.view = new Backform.Dialog({
+-                el: $container,
+-                model: newModel,
+-                schema: fields,
+-              });
+-
+-              $(this.elements.body.childNodes[0]).addClass(
+-                'alertify_tools_dialog_properties obj_properties'
+-              );
+-
+-              view.render();
+-
+-              this.elements.content.appendChild($container.get(0));
+-
+-              view.$el.attr('tabindex', -1);
+-              // var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
+-              pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
+-              var container = view.$el.find('.tab-content:first > .tab-pane.active:first');
+-              commonUtils.findAndSetFocus(container);
+-
+-              // Listen to model & if filename is provided then enable Backup button
+-              this.view.model.on('change', function() {
+-                if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
+-                  this.errorModel.clear();
+-                  self.__internal.buttons[2].element.disabled = false;
+-                } else {
+-                  self.__internal.buttons[2].element.disabled = true;
+-                  this.errorModel.set('file', gettext('Please provide filename'));
+-                }
+-              });
+-
+-            },
+-            // Callback functions when click on the buttons of the Alertify dialogs
+-            callback: function(e) {
+-              // Fetch current server id
+-              var t = pgBrowser.tree,
+-                i = this.settings['pg_item'] || t.selected(),
+-                d = this.settings['pg_item_data'] || (
+-                  i && i.length == 1 ? t.itemData(i) : undefined
+-                ),
+-                node = this.settings['pg_node'] || (
+-                  d && pgBrowser.Nodes[d._type]
+-                );
+-
+-              if (e.button.element.name == 'dialog_help' || e.button.element.name == 'object_help') {
+-                e.cancel = true;
+-                pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+-                  node, i, e.button.element.getAttribute('label'));
+-                return;
+-              }
+-
+-              if (e.button['data-btn-name'] === 'restore') {
+-                if (!d)
+-                  return;
+-
+-                var info = node.getTreeNodeHierarchy.apply(node, [i]),
+-                  m = this.view.model;
+-                // Set current node info into model
+-                m.set('database', info.database._label);
+-                if (!m.get('custom')) {
+-                  switch (d._type) {
+-                  case 'schema':
+-                    m.set('schemas', [d._label]);
+-                    break;
+-                  case 'table':
+-                    m.set('schemas', [info.schema._label]);
+-                    m.set('tables', [d._label]);
+-                    break;
+-                  case 'function':
+-                    m.set('schemas', [info.schema._label]);
+-                    m.set('functions', [d._label]);
+-                    break;
+-                  case 'index':
+-                    m.set('schemas', [info.schema._label]);
+-                    m.set('indexes', [d._label]);
+-                    break;
+-                  case 'trigger':
+-                    m.set('schemas', [info.schema._label]);
+-                    m.set('triggers', [d._label]);
+-                    break;
+-                  case 'trigger_func':
+-                    m.set('schemas', [info.schema._label]);
+-                    m.set('trigger_funcs', [d._label]);
+-                    break;
+-                  }
+-                } else {
+-                  // TODO::
+-                  // When we will implement the object selection in the
+-                  // import dialog, we will need to select the objects from
+-                  // the tree selection tab.
+-                }
+-
+-                var self = this,
+-                  baseUrl = url_for('restore.create_job', {
+-                    'sid': info.server._id,
+-                  }),
+-                  args = this.view.model.toJSON();
+-
+-                $.ajax({
+-                  url: baseUrl,
+-                  method: 'POST',
+-                  data: {
+-                    'data': JSON.stringify(args),
+-                  },
+-                  success: function(res) {
+-                    if (res.success) {
+-                      alertify.success(
+-                        gettext('Restore job created.'), 5
+-                      );
+-                      pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
+-                    } else {
+-                      console.warn(res);
+-                    }
+-                  },
+-                  error: function(xhr) {
+-                    try {
+-                      var err = $.parseJSON(xhr.responseText);
+-                      alertify.alert(
+-                        gettext('Restore failed.'),
+-                        err.errormsg
+-                      );
+-                    } catch (e) {
+-                      console.warn(e.stack || e);
+-                    }
+-                  },
+-                });
+-              }
+-            },
+-          };
+-        });
+-      }
+-
+-      alertify.pg_restore(title, item, data, node).resizeTo('65%', '60%');
++      let dialog = new restoreDialog.RestoreDialog(pgBrowser, $, alertify, RestoreObjectModel);
++      dialog.draw(action, treeItem);
+     },
+   };
+   return pgBrowser.Restore;
diff --git a/web/pgadmin/tools/restore/static/js/restore_dialog.js b/web/pgadmin/tools/restore/static/js/restore_dialog.js
new file mode 100644
index 00000000..4884d901
--- /dev/null
+++ b/web/pgadmin/tools/restore/static/js/restore_dialog.js
@@ -0,0 +1,57 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from '../../../../static/js/gettext';
+import {sprintf} from 'sprintf-js';
+import Backform from '../../../../static/js/backform.pgadmin';
+import {Dialog} from '../../../../static/js/alertify/dialog';
+
+export class RestoreDialog extends Dialog {
+  constructor(pgBrowser, $, alertify, RestoreModel, backform = Backform) {
+    super('Restore Error',
+      '<div class=\'restore_dialog\'></div>',
+      pgBrowser, $, alertify, RestoreModel, backform);
+  }
+
+  draw(action, aciTreeItem) {
+
+    const serverInformation = this.retrieveAncestorOfTypeServer(aciTreeItem);
+
+    if (!serverInformation) {
+      return;
+    }
+
+    if (!this.hasBinariesConfiguration(serverInformation)) {
+      return;
+    }
+
+    if (!this.canExecuteOnCurrentDatabase(aciTreeItem)) {
+      return;
+    }
+
+    let aciTreeItem1 = aciTreeItem || this.pgBrowser.treeMenu.selected();
+    let item = this.pgBrowser.treeMenu.findNodeByDomElement(aciTreeItem1);
+    const data = item.getData();
+    const node = this.pgBrowser.Nodes[data._type];
+
+    if (!node)
+      return;
+
+    let title = sprintf(gettext('Restore (%s: %s)'), node.label, data.label);
+
+    this.createOrGetDialog(title, 'restore');
+
+    this.alertify.pg_restore(title, aciTreeItem1, data, node).resizeTo('65%', '60%');
+  }
+
+  dialogName() {
+    return 'pg_restore';
+  }
+}
+
diff --git a/web/pgadmin/tools/restore/static/js/restore_dialog_wrapper.js b/web/pgadmin/tools/restore/static/js/restore_dialog_wrapper.js
new file mode 100644
index 00000000..845da7a3
--- /dev/null
+++ b/web/pgadmin/tools/restore/static/js/restore_dialog_wrapper.js
@@ -0,0 +1,255 @@
+import {getTreeNodeHierarchyFromElement} from '../../../../static/js/tree/pgadmin_tree_node';
+import axios from 'axios/index';
+import _ from 'underscore';
+import gettext from '../../../../static/js/gettext';
+import url_for from '../../../../static/js/url_for';
+import {DialogWrapper} from '../../../../static/js/alertify/dialog_wrapper';
+
+export class RestoreDialogWrapper extends DialogWrapper {
+  constructor(dialogContainerSelector, dialogTitle, typeOfDialog,
+              jquery, pgBrowser, alertify, dialogModel, backform) {
+    super(dialogContainerSelector, dialogTitle, jquery,
+      pgBrowser, alertify, dialogModel, backform);
+  }
+
+  main(title, item, data, node) {
+    this.set('title', title);
+    this.setting('pg_node', node);
+    this.setting('pg_item', item);
+    this.setting('pg_item_data', data);
+  }
+
+  setup() {
+    return {
+      buttons: [{
+        text: '',
+        className: 'btn btn-default pull-left fa fa-lg fa-info',
+        attrs: {
+          name: 'object_help',
+          type: 'button',
+          url: 'backup.html',
+          label: gettext('Restore'),
+        },
+      }, {
+        text: '',
+        key: 112,
+        className: 'btn btn-default pull-left fa fa-lg fa-question',
+        attrs: {
+          name: 'dialog_help',
+          type: 'button',
+          label: gettext('Restore'),
+          url: url_for('help.static', {
+            'filename': 'restore_dialog.html',
+          }),
+        },
+      }, {
+        text: gettext('Restore'),
+        key: 13,
+        className: 'btn btn-primary fa fa-upload pg-alertify-button',
+        restore: true,
+        'data-btn-name': 'restore',
+      }, {
+        text: gettext('Cancel'),
+        key: 27,
+        className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+        restore: false,
+        'data-btn-name': 'cancel',
+      }],
+      // Set options for dialog
+      options: {
+        title: this.dialogTitle,
+        //disable both padding and overflow control.
+        padding: !1,
+        overflow: !1,
+        model: 0,
+        resizable: true,
+        maximizable: true,
+        pinnable: false,
+        closableByDimmer: false,
+        modal: false,
+      },
+    };
+  }
+
+  prepare() {
+    this.disableRestoreButton();
+
+    const $container = this.jquery(this.dialogContainerSelector);
+    const selectedTreeNode = this.getSelectedNode();
+    const selectedTreeNodeData = this.getSelectedNodeData(selectedTreeNode);
+    if (!selectedTreeNodeData) {
+      return;
+    }
+
+    const node = this.pgBrowser.Nodes[selectedTreeNodeData._type];
+
+    const treeInfo = getTreeNodeHierarchyFromElement(this.pgBrowser, selectedTreeNode);
+    const dialog = this.createDialog(node, treeInfo, $container);
+    this.addAlertifyClassToRestoreNodeChildNodes();
+    dialog.render();
+
+    this.elements.content.appendChild($container.get(0));
+
+    this.focusOnDialog(dialog);
+    this.setListenersForFilenameChanges();
+  }
+
+  callback(event) {
+    const selectedTreeNode = this.getSelectedNode();
+    const selectedTreeNodeData = this.getSelectedNodeData(selectedTreeNode);
+    const node = selectedTreeNodeData && this.pgBrowser.Nodes[selectedTreeNodeData._type];
+
+    if (this.wasHelpButtonPressed(event)) {
+      event.cancel = true;
+      this.pgBrowser.showHelp(
+        event.button.element.name,
+        event.button.element.getAttribute('url'),
+        node,
+        selectedTreeNode,
+        event.button.element.getAttribute('label')
+      );
+      return;
+    }
+
+    if (this.wasRestoreButtonPressed(event)) {
+
+      if (!selectedTreeNodeData)
+        return;
+
+      const serverIdentifier = this.retrieveServerIdentifier(node, selectedTreeNode);
+
+      const dialogWrapper = this;
+      let urlShortcut = 'restore.create_job';
+
+      const baseUrl = url_for(urlShortcut, {
+        'sid': serverIdentifier,
+      });
+
+      const treeInfo = getTreeNodeHierarchyFromElement(
+        this.pgBrowser,
+        selectedTreeNode
+      );
+
+      this.setExtraParameters(selectedTreeNode, treeInfo);
+
+      let service = axios.create({});
+      service.post(
+        baseUrl,
+        this.view.model.toJSON()
+      ).then(function () {
+        dialogWrapper.alertify.success(gettext('Restore job created.'), 5);
+        dialogWrapper.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialogWrapper);
+      }).catch(function (error) {
+        try {
+          const err = error.response.data;
+          dialogWrapper.alertify.alert(
+            gettext('Restore job failed.'),
+            err.errormsg
+          );
+        } catch (e) {
+          console.warn(e.stack || e);
+        }
+      });
+    }
+  }
+
+  addAlertifyClassToRestoreNodeChildNodes() {
+    this.jquery(this.elements.body.childNodes[0]).addClass(
+      'alertify_tools_dialog_properties obj_properties'
+    );
+  }
+
+  getSelectedNode() {
+    const tree = this.pgBrowser.treeMenu;
+    const selectedNode = tree.selected();
+    if (selectedNode) {
+      return tree.findNodeByDomElement(selectedNode);
+    } else {
+      return undefined;
+    }
+  }
+
+  disableRestoreButton() {
+    this.__internal.buttons[2].element.disabled = true;
+  }
+
+  enableRestoreButton() {
+    this.__internal.buttons[2].element.disabled = false;
+  }
+
+  createDialog(node, treeInfo, $container) {
+    const newModel = new this.dialogModel({
+      node_data: node,
+    }, {
+      node_info: treeInfo,
+    });
+    const fields = this.backform.generateViewSchema(
+      treeInfo, newModel, 'create', node, treeInfo.server, true
+    );
+
+    return this.view = new this.backform.Dialog({
+      el: $container,
+      model: newModel,
+      schema: fields,
+    });
+  }
+
+  retrieveServerIdentifier(node, selectedTreeNode) {
+    const treeInfo = getTreeNodeHierarchyFromElement(
+      this.pgBrowser,
+      selectedTreeNode
+    );
+    return treeInfo.server._id;
+  }
+
+  setListenersForFilenameChanges() {
+    const self = this;
+
+    this.view.model.on('change', function () {
+      if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
+        this.errorModel.clear();
+        self.enableRestoreButton();
+      } else {
+        self.disableRestoreButton();
+        this.errorModel.set('file', gettext('Please provide a filename'));
+      }
+    });
+  }
+
+  setExtraParameters(selectedTreeNode, treeInfo) {
+    this.view.model.set('database', treeInfo.database._label);
+    if (!this.view.model.get('custom')) {
+      const nodeData = selectedTreeNode.getData();
+
+      switch (nodeData._type) {
+      case 'schema':
+        this.view.model.set('schemas', [nodeData._label]);
+        break;
+      case 'table':
+        this.view.model.set('schemas', [treeInfo.schema._label]);
+        this.view.model.set('tables', [nodeData._label]);
+        break;
+      case 'function':
+        this.view.model.set('schemas', [treeInfo.schema._label]);
+        this.view.model.set('functions', [nodeData._label]);
+        break;
+      case 'index':
+        this.view.model.set('schemas', [treeInfo.schema._label]);
+        this.view.model.set('indexes', [nodeData._label]);
+        break;
+      case 'trigger':
+        this.view.model.set('schemas', [treeInfo.schema._label]);
+        this.view.model.set('triggers', [nodeData._label]);
+        break;
+      case 'trigger_func':
+        this.view.model.set('schemas', [treeInfo.schema._label]);
+        this.view.model.set('trigger_funcs', [nodeData._label]);
+        break;
+      }
+    }
+  }
+
+  wasRestoreButtonPressed(event) {
+    return event.button['data-btn-name'] === 'restore';
+  }
+}
diff --git a/web/regression/javascript/backup/backup_dialog_spec.js b/web/regression/javascript/backup/backup_dialog_spec.js
new file mode 100644
index 00000000..2655059d
--- /dev/null
+++ b/web/regression/javascript/backup/backup_dialog_spec.js
@@ -0,0 +1,205 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
+import {TreeFake} from '../tree/tree_fake';
+
+const context = describe;
+
+describe('BackupDialog', () => {
+  let backupDialog;
+  let pgBrowser;
+  let jquerySpy;
+  let alertifySpy;
+  let backupModelSpy;
+
+  beforeEach(() => {
+    pgBrowser = {
+      treeMenu: new TreeFake(),
+      Nodes: {
+        server: {
+          hasId: true,
+          label: 'server',
+          getTreeNodeHierarchy: jasmine.createSpy('server.getTreeNodeHierarchy'),
+        },
+        database: {
+          hasId: true,
+          label: 'database',
+          getTreeNodeHierarchy: jasmine.createSpy('db.getTreeNodeHierarchy'),
+        },
+        schema: {
+          hasId: true,
+          label: 'schema',
+          getTreeNodeHierarchy: jasmine.createSpy('db.getTreeNodeHierarchy'),
+        },
+      },
+    };
+    pgBrowser.Nodes.server.hasId = true;
+    pgBrowser.Nodes.database.hasId = true;
+    jquerySpy = jasmine.createSpy('jquerySpy');
+    backupModelSpy = jasmine.createSpy('backupModelSpy');
+
+    const hierarchy = {
+      children: [
+        {
+          id: 'root',
+          children: [
+            {
+              id: 'serverTreeNode',
+              data: {
+                _id: 10,
+                _type: 'server',
+              },
+              children: [
+                {
+                  id: 'some_database',
+                  data: {
+                    _type: 'database',
+                    _id: 11,
+                    label: 'some_database',
+                    _label: 'some_database_label',
+                  },
+                }, {
+                  id: 'database_with_equal_in_name',
+                  data: {
+                    _type: 'database',
+                    label: 'some_database',
+                    _label: '=some_database_label',
+                  },
+                },
+              ],
+            },
+            {
+              id: 'ppasServer',
+              data: {
+                _type: 'server',
+                server_type: 'ppas',
+                children: [
+                  {id: 'someNodeUnderneathPPASServer'},
+                ],
+              },
+            },
+          ],
+        },
+      ],
+    };
+
+    pgBrowser.treeMenu = TreeFake.build(hierarchy);
+  });
+
+  describe('#draw', () => {
+    beforeEach(() => {
+      alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+      alertifySpy['backup_objects'] = jasmine.createSpy('backup_objects');
+      backupDialog = new BackupDialog(
+        pgBrowser,
+        jquerySpy,
+        alertifySpy,
+        backupModelSpy
+      );
+
+      pgBrowser.get_preference = jasmine.createSpy('get_preferences');
+    });
+
+    context('there are no ancestors of the type server', () => {
+      it('does not create a dialog', () => {
+        pgBrowser.treeMenu.selectNode([{id: 'root'}]);
+        backupDialog.draw(null, null, null);
+        expect(alertifySpy['backup_objects']).not.toHaveBeenCalled();
+      });
+
+      it('display an alert with a Backup Error', () => {
+        backupDialog.draw(null, [{id: 'root'}], null);
+        expect(alertifySpy.alert).toHaveBeenCalledWith(
+          'Backup Error',
+          'Please select server or child node from the browser tree.'
+        );
+      });
+    });
+
+    context('there is an ancestor of the type server', () => {
+      context('no preference can be found', () => {
+        beforeEach(() => {
+          pgBrowser.get_preference.and.returnValue(undefined);
+        });
+
+        context('server is a ppas server', () => {
+          it('display an alert with "Backup Error"', () => {
+            backupDialog.draw(null, [{id: 'some_database'}], null);
+            expect(alertifySpy.alert).toHaveBeenCalledWith(
+              'Backup Error',
+              'Failed to load preference pg_bin_dir of module paths'
+            );
+          });
+        });
+
+        context('server is not a ppas server', () => {
+          it('display an alert with "Backup Error"', () => {
+            backupDialog.draw(null, [{id: 'ppasServer'}], null);
+            expect(alertifySpy.alert).toHaveBeenCalledWith(
+              'Backup Error',
+              'Failed to load preference ppas_bin_dir of module paths'
+            );
+          });
+        });
+      });
+
+      context('preference can be found', () => {
+        context('binary folder is not configured', () => {
+          beforeEach(() => {
+            pgBrowser.get_preference.and.returnValue({});
+          });
+
+          context('server is a ppas server', () => {
+            it('display an alert with "Configuration required"', () => {
+              backupDialog.draw(null, [{id: 'serverTreeNode'}], null);
+              expect(alertifySpy.alert).toHaveBeenCalledWith(
+                'Configuration required',
+                'Please configure the PostgreSQL Binary Path in the Preferences dialog.'
+              );
+            });
+          });
+
+          context('server is not a ppas server', () => {
+            it('display an alert with "Configuration required"', () => {
+              backupDialog.draw(null, [{id: 'ppasServer'}], null);
+              expect(alertifySpy.alert).toHaveBeenCalledWith(
+                'Configuration required',
+                'Please configure the EDB Advanced Server Binary Path in the Preferences dialog.'
+              );
+            });
+          });
+        });
+
+        context('binary folder is configured', () => {
+          let backupDialogResizeToSpy;
+          beforeEach(() => {
+            backupDialogResizeToSpy = jasmine.createSpyObj('backupDialogResizeToSpy', ['resizeTo']);
+            alertifySpy['backup_objects'].and
+              .returnValue(backupDialogResizeToSpy);
+            pgBrowser.get_preference.and.returnValue({value: '/some/path'});
+          });
+
+          it('displays the dialog', () => {
+            backupDialog.draw(null, [{id: 'serverTreeNode'}], null);
+            expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true);
+            expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
+          });
+
+          context('database label contain "="', () => {
+            it('should create alert dialog with backup error', () => {
+              backupDialog.draw(null, [{id: 'database_with_equal_in_name'}], null);
+              expect(alertifySpy.alert).toHaveBeenCalledWith('Backup Error',
+                'Databases with = symbols in the name cannot be backed up or restored using this utility.');
+            });
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/backup/backup_dialog_wrapper_spec.js b/web/regression/javascript/backup/backup_dialog_wrapper_spec.js
new file mode 100644
index 00000000..58705318
--- /dev/null
+++ b/web/regression/javascript/backup/backup_dialog_wrapper_spec.js
@@ -0,0 +1,675 @@
+import {TreeFake} from '../tree/tree_fake';
+import {BackupDialogWrapper} from '../../../pgadmin/tools/backup/static/js/backup_dialog_wrapper';
+import axios from 'axios/index';
+import MockAdapter from 'axios-mock-adapter';
+import {FakeModel} from '../fake_model';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+let context = describe;
+
+describe('BackupDialogWrapper', () => {
+  let jquerySpy;
+  let pgBrowser;
+  let alertifySpy;
+  let dialogModelKlassSpy;
+  let backform;
+  let generatedBackupModel;
+  let backupDialogWrapper;
+  let noDataNode;
+  let serverTreeNode;
+  let databaseTreeNode;
+  let viewSchema;
+  let backupJQueryContainerSpy;
+  let backupNodeChildNodeSpy;
+  let backupNode;
+
+  beforeEach(() => {
+    pgBrowser = {
+      treeMenu: new TreeFake(),
+      Nodes: {
+        server: {
+          hasId: true,
+          getTreeNodeHierarchy: jasmine.createSpy('getTreeNodeHierarchy'),
+        },
+        database: {
+          hasId: true,
+        },
+      },
+      keyboardNavigation: jasmine.createSpyObj('keyboardNavigation', ['getDialogTabNavigator']),
+    };
+    noDataNode = pgBrowser.treeMenu.addNewNode('level1.1', undefined, [{id: 'level1'}]);
+    serverTreeNode = pgBrowser.treeMenu.addNewNode('level2.1', {
+      _type: 'server',
+      _id: 10,
+      label: 'some-tree-label',
+    }, [{id: 'level2.1'}]);
+    databaseTreeNode = new TreeNode('database-tree-node', {
+      _type: 'database',
+      _label: 'some-database-label',
+    }, [{id: 'database-tree-node'}]);
+    pgBrowser.treeMenu.addChild(serverTreeNode, databaseTreeNode);
+
+    jquerySpy = jasmine.createSpy('jquerySpy');
+    backupNode = {
+      __internal: {
+        buttons: [{}, {}, {
+          element: {
+            disabled: false,
+          },
+        }],
+      },
+      elements: {
+        body: {
+          childNodes: [
+            {},
+          ],
+        },
+        content: jasmine.createSpyObj('content', ['appendChild', 'attr']),
+      },
+    };
+
+    backupJQueryContainerSpy = jasmine.createSpyObj('backupJQueryContainer', ['get', 'attr']);
+    backupJQueryContainerSpy.get.and.returnValue(backupJQueryContainerSpy);
+
+    generatedBackupModel = {};
+    dialogModelKlassSpy = jasmine.createSpy('dialogModelKlass');
+    dialogModelKlassSpy.and.returnValue(generatedBackupModel);
+
+    viewSchema = {};
+    backform = jasmine.createSpyObj('backform', ['generateViewSchema', 'Dialog']);
+    backform.generateViewSchema.and.returnValue(viewSchema);
+
+    backupNodeChildNodeSpy = jasmine.createSpyObj('something', ['addClass']);
+    jquerySpy.and.callFake((selector) => {
+      if (selector === '<div class=\'backup_dialog\'></div>') {
+        return backupJQueryContainerSpy;
+      } else if (selector === backupNode.elements.body.childNodes[0]) {
+        return backupNodeChildNodeSpy;
+      }
+    });
+
+  });
+
+  describe('#prepare', () => {
+    beforeEach(() => {
+      alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+      backupDialogWrapper = new BackupDialogWrapper(
+        '<div class=\'backup_dialog\'></div>',
+        'backupDialogTitle',
+        'backup',
+        jquerySpy,
+        pgBrowser,
+        alertifySpy,
+        dialogModelKlassSpy,
+        backform
+      );
+      backupDialogWrapper = Object.assign(backupDialogWrapper, backupNode);
+    });
+
+    context('no tree element is selected', () => {
+      it('does not create a backform dialog', () => {
+        backupDialogWrapper.prepare();
+        expect(backform.Dialog).not.toHaveBeenCalled();
+      });
+
+      it('disables the button "submit button" until a filename is selected', () => {
+        backupDialogWrapper.prepare();
+        expect(backupDialogWrapper.__internal.buttons[2].element.disabled).toBe(true);
+      });
+    });
+
+    context('selected tree node has no data', () => {
+      beforeEach(() => {
+        pgBrowser.treeMenu.selectNode(noDataNode.domNode);
+      });
+
+      it('does not create a backform dialog', () => {
+        backupDialogWrapper.prepare();
+        expect(backform.Dialog).not.toHaveBeenCalled();
+      });
+
+      it('disables the button "submit button" until a filename is selected', () => {
+        backupDialogWrapper.prepare();
+        expect(backupDialogWrapper.__internal.buttons[2].element.disabled).toBe(true);
+      });
+    });
+
+    context('tree element is selected', () => {
+      let treeHierarchyInformation;
+      let dialogSpy;
+
+      beforeEach(() => {
+        treeHierarchyInformation = {
+          server: {
+            _type: 'server',
+            _id: 10,
+            priority: 0,
+            label: 'some-tree-label',
+          },
+        };
+        pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+        pgBrowser.Nodes['server'].getTreeNodeHierarchy.and
+          .returnValue(treeHierarchyInformation);
+        dialogSpy = jasmine.createSpyObj('newView', ['render']);
+        dialogSpy.$el = jasmine.createSpyObj('$el', ['find', 'attr']);
+        dialogSpy.model = jasmine.createSpyObj('model', ['on']);
+        dialogSpy.$el.find.and.returnValue([]);
+
+        backform.Dialog.and.returnValue(dialogSpy);
+      });
+
+      it('creates a backform dialog and displays it', () => {
+        backupDialogWrapper.prepare();
+        expect(backform.Dialog).toHaveBeenCalledWith({
+          el: backupJQueryContainerSpy,
+          model: generatedBackupModel,
+          schema: viewSchema,
+        });
+
+        expect(dialogSpy.render).toHaveBeenCalled();
+      });
+
+
+      it('add alertify classes to restore node childnode', () => {
+        backupDialogWrapper.prepare();
+        expect(backupNodeChildNodeSpy.addClass)
+          .toHaveBeenCalledWith('alertify_tools_dialog_properties obj_properties');
+      });
+
+      it('disables the button submit button until a filename is selected', () => {
+        backupDialogWrapper.prepare();
+        expect(backupNode.__internal.buttons[2].element.disabled).toBe(true);
+      });
+
+      it('generates a new backup model', () => {
+        backupDialogWrapper.prepare();
+        expect(dialogModelKlassSpy).toHaveBeenCalledWith(
+          {type: 'backup'},
+          {node_info: treeHierarchyInformation}
+        );
+      });
+
+      it('add the new dialog to the backup node HTML', () => {
+        backupDialogWrapper.prepare();
+        expect(backupNode.elements.content.appendChild).toHaveBeenCalledWith(backupJQueryContainerSpy);
+      });
+    });
+  });
+
+  describe('onButtonClicked', () => {
+    let networkMock;
+    beforeEach(() => {
+      networkMock = new MockAdapter(axios);
+      backupDialogWrapper = new BackupDialogWrapper(
+        '<div class=\'backup_dialog\'></div>',
+        'backupDialogTitle',
+        'backup',
+        jquerySpy,
+        pgBrowser,
+        alertifySpy,
+        dialogModelKlassSpy,
+        backform
+      );
+
+      backupDialogWrapper = Object.assign(backupDialogWrapper, backupNode);
+    });
+
+    afterEach(() => {
+      networkMock.restore();
+    });
+
+    context('dialog help button was pressed', () => {
+      let networkCalled;
+      beforeEach(() => {
+        networkCalled = false;
+        networkMock.onAny(/.*/).reply(() => {
+          networkCalled = true;
+          return [200, {}];
+        });
+        pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+        pgBrowser.showHelp = jasmine.createSpy('showHelp');
+
+        const event = {
+          button: {
+            element: {
+              name: 'dialog_help',
+              getAttribute: (attributeName) => {
+                if (attributeName === 'url') {
+                  return 'http://someurl';
+                } else if (attributeName === 'label') {
+                  return 'some label';
+                }
+              },
+            },
+          },
+        };
+        backupDialogWrapper.callback(event);
+      });
+
+      it('displays help for dialog', () => {
+        expect(pgBrowser.showHelp).toHaveBeenCalledWith(
+          'dialog_help',
+          'http://someurl',
+          pgBrowser.Nodes['server'],
+          serverTreeNode,
+          'some label'
+        );
+      });
+
+      it('does not start the backup', () => {
+        expect(networkCalled).toBe(false);
+      });
+    });
+
+    context('object help button was pressed', () => {
+      let networkCalled;
+      beforeEach(() => {
+        networkCalled = false;
+        networkMock.onAny(/.*/).reply(() => {
+          networkCalled = true;
+          return [200, {}];
+        });
+        pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+        pgBrowser.showHelp = jasmine.createSpy('showHelp');
+
+        const event = {
+          button: {
+            element: {
+              name: 'object_help',
+              getAttribute: (attributeName) => {
+                if (attributeName === 'url') {
+                  return 'http://someurl';
+                } else if (attributeName === 'label') {
+                  return 'some label';
+                }
+              },
+            },
+          },
+        };
+        backupDialogWrapper.callback(event);
+      });
+
+      it('displays help for dialog', () => {
+        expect(pgBrowser.showHelp).toHaveBeenCalledWith(
+          'object_help',
+          'http://someurl',
+          pgBrowser.Nodes['server'],
+          serverTreeNode,
+          'some label'
+        );
+      });
+
+      it('does not start the backup', () => {
+        expect(networkCalled).toBe(false);
+      });
+    });
+
+    context('backup button was pressed', () => {
+      context('no tree node is selected', () => {
+        it('does not start the backup', () => {
+          let networkCalled = false;
+          networkMock.onAny(/.*/).reply(() => {
+            networkCalled = true;
+            return [200, {}];
+          });
+
+          let event = {
+            button: {
+              'data-btn-name': 'backup',
+              element: {
+                getAttribute: () => {
+                  return 'http://someurl';
+                },
+              },
+            },
+          };
+
+          backupDialogWrapper.callback(event);
+          expect(networkCalled).toBe(false);
+        });
+      });
+
+      context('tree node has no data', () => {
+        it('does not start the backup', () => {
+          pgBrowser.treeMenu.selectNode(noDataNode.domNode);
+
+          let networkCalled = false;
+          networkMock.onAny(/.*/).reply(() => {
+            networkCalled = true;
+            return [200, {}];
+          });
+
+          let event = {
+            button: {
+              'data-btn-name': 'backup',
+              element: {
+                getAttribute: () => {
+                  return 'http://someurl';
+                },
+              },
+            },
+          };
+
+          backupDialogWrapper.callback(event);
+          expect(networkCalled).toBe(false);
+        });
+      });
+
+      context('tree node has data', () => {
+        context('when dialog type is global', () => {
+          let event;
+          beforeEach(() => {
+            pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+
+            backupDialogWrapper.view = {
+              model: new FakeModel(),
+            };
+
+            event = {
+              button: {
+                'data-btn-name': 'backup',
+                element: {
+                  getAttribute: () => {
+                    return 'http://someurl';
+                  },
+                },
+              },
+            };
+          });
+
+          context('when the backup job is created successfully', () => {
+            let dataSentToServer;
+            beforeEach(() => {
+              pgBrowser.Events = jasmine.createSpyObj('Events', ['trigger']);
+              alertifySpy.success = jasmine.createSpy('success');
+
+              networkMock.onPost('/backup/job/10').reply((request) => {
+                dataSentToServer = request.data;
+                return [200, {}];
+              });
+            });
+
+            it('creates a success alert box', (done) => {
+              backupDialogWrapper.callback(event);
+              setTimeout(() => {
+                expect(alertifySpy.success).toHaveBeenCalledWith(
+                  'Backup job created.',
+                  5
+                );
+                done();
+              }, 0);
+            });
+
+            it('trigger an event to background process', (done) => {
+              backupDialogWrapper.callback(event);
+
+              setTimeout(() => {
+                expect(pgBrowser.Events.trigger).toHaveBeenCalledWith(
+                  'pgadmin-bgprocess:created',
+                  backupDialogWrapper
+                );
+                done();
+              }, 0);
+            });
+
+            it('send the correct paramenters to the backend', (done) => {
+              backupDialogWrapper.callback(event);
+              setTimeout(() => {
+                expect(JSON.parse(dataSentToServer)).toEqual(
+                  {}
+                );
+                done();
+              }, 0);
+            });
+          });
+
+          context('when creating backup job fails', () => {
+            it('creates an alert box', (done) => {
+              alertifySpy.alert = jasmine.createSpy('alert');
+              networkMock.onPost('/backup/job/10').reply(() => {
+                return [400, {
+                  errormsg: 'some-error-message',
+                }];
+              });
+
+              backupDialogWrapper.callback(event);
+              setTimeout(() => {
+                expect(alertifySpy.alert).toHaveBeenCalledWith(
+                  'Backup job failed.',
+                  'some-error-message'
+                );
+                done();
+              }, 0);
+
+            });
+          });
+        });
+
+        context('when dialog type is object', () => {
+          let event;
+          beforeEach(() => {
+            backupDialogWrapper = new BackupDialogWrapper(
+              '<div class=\'backup_dialog\'></div>',
+              'backupDialogTitle',
+              'backup_objects',
+              jquerySpy,
+              pgBrowser,
+              alertifySpy,
+              dialogModelKlassSpy,
+              backform
+            );
+
+            pgBrowser.treeMenu.selectNode(databaseTreeNode.domNode);
+
+            backupDialogWrapper.view = {
+              model: new FakeModel(),
+            };
+
+            event = {
+              button: {
+                'data-btn-name': 'backup',
+                element: {
+                  getAttribute: () => {
+                    return 'http://someurl';
+                  },
+                },
+              },
+            };
+          });
+
+          context('when the backup job is created successfully', () => {
+            let dataSentToServer;
+            beforeEach(() => {
+              pgBrowser.Events = jasmine.createSpyObj('Events', ['trigger']);
+              alertifySpy.success = jasmine.createSpy('success');
+
+              networkMock.onPost('/backup/job/10/object').reply((request) => {
+                dataSentToServer = request.data;
+                return [200, {}];
+              });
+            });
+
+            it('creates a success alert box', (done) => {
+              backupDialogWrapper.callback(event);
+              setTimeout(() => {
+                expect(alertifySpy.success).toHaveBeenCalledWith(
+                  'Backup job created.',
+                  5
+                );
+                done();
+              }, 0);
+            });
+
+            it('trigger an event to background process', (done) => {
+              backupDialogWrapper.callback(event);
+
+              setTimeout(() => {
+                expect(pgBrowser.Events.trigger).toHaveBeenCalledWith(
+                  'pgadmin-bgprocess:created',
+                  backupDialogWrapper
+                );
+                done();
+              }, 0);
+            });
+
+            it('send the correct parameters to the backend', (done) => {
+              backupDialogWrapper.callback(event);
+              setTimeout(() => {
+                expect(JSON.parse(dataSentToServer)).toEqual(
+                  {database: 'some-database-label'}
+                );
+                done();
+              }, 0);
+            });
+          });
+
+          context('when creating backup job fails', () => {
+            it('creates an alert box', (done) => {
+              alertifySpy.alert = jasmine.createSpy('alert');
+              networkMock.onPost('/backup/job/10/object').reply(() => {
+                return [400, {
+                  errormsg: 'some-error-message',
+                }];
+              });
+
+              backupDialogWrapper.callback(event);
+              setTimeout(() => {
+                expect(alertifySpy.alert).toHaveBeenCalledWith(
+                  'Backup job failed.',
+                  'some-error-message'
+                );
+                done();
+              }, 0);
+            });
+          });
+        });
+      });
+    });
+  });
+
+  describe('#setExtraParameters', () => {
+    let selectedTreeNode;
+    let treeInfo;
+    let model;
+
+    context('when dialog type is global', () => {
+      beforeEach(() => {
+        backupDialogWrapper = new BackupDialogWrapper(
+          '<div class=\'backup_dialog\'></div>',
+          'backupDialogTitle',
+          'backup',
+          jquerySpy,
+          pgBrowser,
+          alertifySpy,
+          dialogModelKlassSpy,
+          backform
+        );
+
+        treeInfo = {};
+        model = new FakeModel();
+        backupDialogWrapper.view = {
+          model: model,
+        };
+      });
+
+
+      it('sets nothing on the view model', () => {
+        backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+        expect(model.toJSON()).toEqual({});
+      });
+    });
+
+    context('when dialog type is object', () => {
+      beforeEach(() => {
+        backupDialogWrapper = new BackupDialogWrapper(
+          '<div class=\'backup_dialog\'></div>',
+          'backupDialogTitle',
+          'backup_objects',
+          jquerySpy,
+          pgBrowser,
+          alertifySpy,
+          dialogModelKlassSpy,
+          backform
+        );
+
+        treeInfo = {
+          database: {
+            _label: 'some-database-label',
+          },
+          schema: {
+            _label: 'some-treeinfo-label',
+          },
+        };
+
+        model = new FakeModel();
+        selectedTreeNode = new TreeNode('some-selected-node',
+          {_type: 'some-type', _label: 'some-selected-label'},
+          []);
+        backupDialogWrapper.view = {
+          model: model,
+        };
+      });
+
+      it('sets the database label on the model', () => {
+        backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+        expect(model.toJSON()).toEqual({
+          'database': 'some-database-label',
+        });
+      });
+
+      context('when the selected is a schema type', () => {
+        beforeEach(() => {
+          selectedTreeNode = new TreeNode('some-selected-node',
+            {_type: 'schema', _label: 'some-schema-label'},
+            []);
+        });
+
+        it('sets the schema label on the model', () => {
+          backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+          expect(model.toJSON()).toEqual({
+            'database': 'some-database-label',
+            'schemas': ['some-schema-label'],
+          });
+        });
+      });
+
+      context('when the selected is a table type', () => {
+        beforeEach(() => {
+          selectedTreeNode = new TreeNode('some-selected-node',
+            {_type: 'table', _label: 'some-table-label'},
+            []);
+        });
+
+        it('sets the schema label on the model', () => {
+          backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+          expect(model.toJSON()).toEqual({
+            'database': 'some-database-label',
+            'tables': [['some-treeinfo-label', 'some-table-label']],
+          });
+        });
+      });
+
+      context('when the model has no ratio value', () => {
+        beforeEach(() => {
+          model.set('ratio', '');
+        });
+
+        it('sets clears the ratio value', () => {
+          backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+          expect(model.get('ratio')).toBeUndefined();
+        });
+      });
+
+      context('when the model has a valid ratio value', () => {
+        beforeEach(() => {
+          model.set('ratio', '0.25');
+        });
+
+        it('sets clears the ratio value', () => {
+          backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo);
+          expect(model.get('ratio')).toEqual('0.25');
+        });
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/backup/global_server_backup_dialog_spec.js b/web/regression/javascript/backup/global_server_backup_dialog_spec.js
new file mode 100644
index 00000000..86df672e
--- /dev/null
+++ b/web/regression/javascript/backup/global_server_backup_dialog_spec.js
@@ -0,0 +1,168 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
+import {TreeFake} from '../tree/tree_fake';
+
+const context = describe;
+
+describe('GlobalServerBackupDialog', () => {
+  let backupDialog;
+  let pgBrowser;
+  let jquerySpy;
+  let alertifySpy;
+  let backupModelSpy;
+
+
+  let rootNode;
+  let serverTreeNode;
+  let ppasServerTreeNode;
+
+  beforeEach(() => {
+    pgBrowser = {
+      treeMenu: new TreeFake(),
+      Nodes: {
+        server: jasmine.createSpyObj('Node[server]', ['getTreeNodeHierarchy']),
+      },
+    };
+    pgBrowser.Nodes.server.hasId = true;
+    jquerySpy = jasmine.createSpy('jquerySpy');
+    backupModelSpy = jasmine.createSpy('backupModelSpy');
+
+    rootNode = pgBrowser.treeMenu.addNewNode('level1', {}, undefined, []);
+    serverTreeNode = pgBrowser.treeMenu.addNewNode('level1.1', {
+      _type: 'server',
+      _id: 10,
+    }, undefined, ['level1']);
+    ppasServerTreeNode = pgBrowser.treeMenu.addNewNode('level1.2', {
+      _type: 'server',
+      server_type: 'ppas',
+    }, undefined, ['level1']);
+    pgBrowser.treeMenu.addNewNode('level3', {}, undefined, ['level1', 'level1.2']);
+    pgBrowser.treeMenu.addNewNode('level3.1', undefined, undefined, ['level1', 'level1.2', 'level3']);
+  });
+
+  describe('#draw', () => {
+    beforeEach(() => {
+      alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+      alertifySpy['BackupDialog_globals'] = jasmine.createSpy('BackupDialog_globals');
+      alertifySpy['BackupDialog_server'] = jasmine.createSpy('BackupDialog_server');
+      backupDialog = new BackupDialog(
+        pgBrowser,
+        jquerySpy,
+        alertifySpy,
+        backupModelSpy
+      );
+
+      pgBrowser.get_preference = jasmine.createSpy('get_preferences');
+    });
+
+    context('there are no ancestors of the type server', () => {
+      it('does not create a dialog', () => {
+        pgBrowser.treeMenu.selectNode([{id: 'level1'}]);
+        backupDialog.draw(null, null, null);
+        expect(alertifySpy['BackupDialog_globals']).not.toHaveBeenCalled();
+        expect(alertifySpy['BackupDialog_server']).not.toHaveBeenCalled();
+      });
+
+      it('display an alert with a Backup Error', () => {
+        backupDialog.draw(null, [rootNode], null);
+        expect(alertifySpy.alert).toHaveBeenCalledWith(
+          'Backup Error',
+          'Please select server or child node from the browser tree.'
+        );
+      });
+    });
+
+    context('there is an ancestor of the type server', () => {
+      context('no preference can be found', () => {
+        beforeEach(() => {
+          pgBrowser.get_preference.and.returnValue(undefined);
+        });
+
+        context('server is a ppas server', () => {
+          it('display an alert with "Backup Error"', () => {
+            backupDialog.draw(null, [serverTreeNode], null);
+            expect(alertifySpy.alert).toHaveBeenCalledWith(
+              'Backup Error',
+              'Failed to load preference pg_bin_dir of module paths'
+            );
+          });
+        });
+
+        context('server is not a ppas server', () => {
+          it('display an alert with "Backup Error"', () => {
+            backupDialog.draw(null, [ppasServerTreeNode], null);
+            expect(alertifySpy.alert).toHaveBeenCalledWith(
+              'Backup Error',
+              'Failed to load preference ppas_bin_dir of module paths'
+            );
+          });
+        });
+      });
+
+      context('preference can be found', () => {
+        context('binary folder is not configured', () => {
+          beforeEach(() => {
+            pgBrowser.get_preference.and.returnValue({});
+          });
+
+          context('server is a ppas server', () => {
+            it('display an alert with "Configuration required"', () => {
+              backupDialog.draw(null, [serverTreeNode], null);
+              expect(alertifySpy.alert).toHaveBeenCalledWith(
+                'Configuration required',
+                'Please configure the PostgreSQL Binary Path in the Preferences dialog.'
+              );
+            });
+          });
+
+          context('server is not a ppas server', () => {
+            it('display an alert with "Configuration required"', () => {
+              backupDialog.draw(null, [ppasServerTreeNode], null);
+              expect(alertifySpy.alert).toHaveBeenCalledWith(
+                'Configuration required',
+                'Please configure the EDB Advanced Server Binary Path in the Preferences dialog.'
+              );
+            });
+          });
+        });
+
+        context('binary folder is configured', () => {
+          let globalResizeToSpy;
+          let serverResizeToSpy;
+          beforeEach(() => {
+            globalResizeToSpy = jasmine.createSpyObj('globals', ['resizeTo']);
+            alertifySpy['BackupDialog_globals'].and
+              .returnValue(globalResizeToSpy);
+            serverResizeToSpy = jasmine.createSpyObj('server', ['resizeTo']);
+            alertifySpy['BackupDialog_server'].and
+              .returnValue(serverResizeToSpy);
+            pgBrowser.get_preference.and.returnValue({value: '/some/path'});
+          });
+
+          context('dialog for global backup', () => {
+            it('displays the dialog', () => {
+              backupDialog.draw(null, [serverTreeNode], {globals: true});
+              expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true);
+              expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
+            });
+          });
+
+          context('dialog for server backup', () => {
+            it('displays the dialog', () => {
+              backupDialog.draw(null, [serverTreeNode], {server: true});
+              expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true);
+              expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
+            });
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/backup/menu_utils_spec.js b/web/regression/javascript/backup/menu_utils_spec.js
new file mode 100644
index 00000000..9435d699
--- /dev/null
+++ b/web/regression/javascript/backup/menu_utils_spec.js
@@ -0,0 +1,55 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+
+import {menuEnabledServer} from '../../../pgadmin/tools/backup/static/js/menu_utils';
+
+const context = describe;
+
+describe('backup.menuUtils', () => {
+  describe('#menuEnabledServer', () => {
+    context('provided node data is undefined', () => {
+      it('returns false', () => {
+        expect(menuEnabledServer(undefined)).toBe(false);
+      });
+    });
+
+    context('provided node data is null', () => {
+      it('returns false', () => {
+        expect(menuEnabledServer(null)).toBe(false);
+      });
+    });
+
+    context('current node type is not of the type server', () => {
+      it('returns false', () => {
+        expect(menuEnabledServer({_type: 'schema'})).toBe(false);
+      });
+    });
+
+    context('current node type is of the type server', () => {
+      context('is connected', () => {
+        it('returns true', () => {
+          expect(menuEnabledServer({
+            _type: 'server',
+            connected: true,
+          })).toBe(true);
+        });
+      });
+      context('is not connected', () => {
+        it('returns false', () => {
+          expect(menuEnabledServer({
+            _type: 'server',
+            connected: false,
+          })).toBe(false);
+        });
+      });
+    });
+  });
+});
+
diff --git a/web/regression/javascript/common_keyboard_shortcuts_spec.js b/web/regression/javascript/common_keyboard_shortcuts_spec.js
index 9ea31efd..e27929bf 100644
--- a/web/regression/javascript/common_keyboard_shortcuts_spec.js
+++ b/web/regression/javascript/common_keyboard_shortcuts_spec.js
@@ -11,10 +11,6 @@ import keyboardShortcuts from 'sources/keyboard_shortcuts';
 
 describe('the keyboard shortcuts', () => {
   const F1_KEY = 112;
-  // const EDIT_KEY = 71;  // Key: G -> Grid values
-  // const LEFT_ARROW_KEY = 37;
-  // const RIGHT_ARROW_KEY = 39;
-  // const MOVE_NEXT = 'right';
 
   let debuggerElementSpy, event, debuggerUserShortcutSpy;
   debuggerUserShortcutSpy = jasmine.createSpyObj(
diff --git a/web/regression/javascript/datagrid/get_panel_title_spec.js b/web/regression/javascript/datagrid/get_panel_title_spec.js
new file mode 100644
index 00000000..8a344a84
--- /dev/null
+++ b/web/regression/javascript/datagrid/get_panel_title_spec.js
@@ -0,0 +1,82 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {getPanelTitle} from '../../../pgadmin/tools/datagrid/static/js/get_panel_title';
+import {TreeFake} from '../tree/tree_fake';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+const context = describe;
+
+describe('#getPanelTitle', () => {
+  let pgBrowser;
+  let tree;
+  beforeEach(() => {
+    tree = new TreeFake();
+    pgBrowser = {
+      treeMenu: tree,
+      Nodes: {
+        server: {
+          hasId: true,
+          _type: 'server',
+        },
+        database: {
+          hasId: true,
+          _type: 'database',
+        },
+      },
+    };
+  });
+
+  context('selected node does not belong to a server', () => {
+    it('returns undefined', () => {
+      const root = tree.addNewNode('level1', {_type: 'server_groups'});
+      tree.addChild(root, new TreeNode('level1.1', {_type: 'other'}));
+      tree.selectNode([{id: 'level1'}]);
+      expect(getPanelTitle(pgBrowser)).toBeUndefined();
+    });
+  });
+
+  context('selected node belong to a server', () => {
+    context('selected node does not belong to a database', () => {
+      it('returns the server label and the username', () => {
+        tree.addNewNode('level1', {
+          _type: 'server',
+          db: 'other db label',
+          user: {name: 'some user name'},
+          label: 'server label',
+        }, []);
+
+        tree.selectNode([{id: 'level1'}]);
+        expect(getPanelTitle(pgBrowser))
+          .toBe('other db label on some user name@server label');
+      });
+    });
+
+    context('selected node belongs to a database', () => {
+      it('returns the database label and the username', () => {
+        const root = tree.addNewNode('level1', {
+          _type: 'server',
+          db: 'other db label',
+          user: {name: 'some user name'},
+          label: 'server label',
+        });
+        const level1 = new TreeNode('level1.1', {
+          _type: 'database',
+          label: 'db label',
+        });
+        tree.addChild(root, level1);
+        tree.addChild(level1,
+          new TreeNode('level1.1.1', {_type: 'table'}));
+        tree.selectNode([{id: 'level1.1.1'}]);
+        expect(getPanelTitle(pgBrowser))
+          .toBe('db label on some user name@server label');
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/datagrid/show_data_spec.js b/web/regression/javascript/datagrid/show_data_spec.js
new file mode 100644
index 00000000..80d25eb3
--- /dev/null
+++ b/web/regression/javascript/datagrid/show_data_spec.js
@@ -0,0 +1,171 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {showDataGrid} from '../../../pgadmin/tools/datagrid/static/js/show_data';
+import {TreeFake} from '../tree/tree_fake';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+const context = describe;
+
+describe('#show_data', () => {
+  let datagrid;
+  let pgBrowser;
+  let alertify;
+  beforeEach(() => {
+    alertify = jasmine.createSpyObj('alertify', ['alert']);
+    datagrid = {
+      create_transaction: jasmine.createSpy('create_transaction'),
+    };
+    pgBrowser = {
+      treeMenu: new TreeFake(),
+      Nodes: {
+        server_group: {
+          _type: 'server_group',
+          hasId: true,
+        },
+        server: {
+          _type: 'server',
+          hasId: true,
+        },
+        database: {
+          _type: 'database',
+          hasId: true,
+        },
+        schema: {
+          _type: 'schema',
+          hasId: true,
+        },
+        view: {
+          _type: 'view',
+          hasId: true,
+        },
+        catalog: {
+          _type: 'catalog',
+          hasId: true,
+        },
+      },
+    };
+    const parent = pgBrowser.treeMenu.addNewNode('parent', {_type: 'parent'}, []);
+    const serverGroup1 = new TreeNode('server_group1', {
+      _type: 'server_group',
+      _id: 1,
+    });
+    pgBrowser.treeMenu.addChild(parent, serverGroup1);
+
+    const server1 = new TreeNode('server1', {
+      _type: 'server',
+      label: 'server1',
+      server_type: 'pg',
+      _id: 2,
+    }, ['parent', 'server_group1']);
+    pgBrowser.treeMenu.addChild(serverGroup1, server1);
+
+    const database1 = new TreeNode('database1', {
+      _type: 'database',
+      label: 'database1',
+      _id: 3,
+    }, ['parent', 'server_group1', 'server1']);
+    pgBrowser.treeMenu.addChild(server1, database1);
+
+    const schema1 = new TreeNode('schema1', {
+      _type: 'schema',
+      label: 'schema1',
+      _id: 4,
+    });
+    pgBrowser.treeMenu.addChild(database1, schema1);
+
+    const view1 = new TreeNode('view1', {
+      _type: 'view',
+      label: 'view1',
+      _id: 5,
+    }, ['parent', 'server_group1', 'server1', 'database1']);
+    pgBrowser.treeMenu.addChild(database1, view1);
+
+    const catalog1 = new TreeNode('catalog1', {
+      _type: 'catalog',
+      label: 'catalog1',
+      _id: 6,
+    }, ['parent', 'server_group1', 'server1', 'database1']);
+    pgBrowser.treeMenu.addChild(database1, catalog1);
+  });
+
+  context('cannot find the tree node', () => {
+    it('does not create a transaction', () => {
+      showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}]);
+      expect(datagrid.create_transaction).not.toHaveBeenCalled();
+    });
+
+    it('display alert', () => {
+      showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}]);
+      expect(alertify.alert).toHaveBeenCalledWith(
+        'Data Grid Error',
+        'No object selected.'
+      );
+    });
+  });
+
+  context('current node is not underneath a server', () => {
+    it('does not create a transaction', () => {
+      showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'parent'}]);
+      expect(datagrid.create_transaction).not.toHaveBeenCalled();
+    });
+  });
+
+  context('current node is not underneath a schema or view or catalog', () => {
+    it('does not create a transaction', () => {
+      showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'database1'}]);
+      expect(datagrid.create_transaction).not.toHaveBeenCalled();
+    });
+  });
+
+  context('current node is underneath a schema', () => {
+    it('does not create a transaction', () => {
+      showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'schema1'}]);
+      expect(datagrid.create_transaction).toHaveBeenCalledWith(
+        '/initialize/datagrid/11/schema/1/2/3/4',
+        null,
+        'false',
+        'pg',
+        '',
+        'server1 - database1 - schema1.schema1',
+        ''
+      );
+    });
+  });
+
+  context('current node is underneath a view', () => {
+    it('does not create a transaction', () => {
+      showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'view1'}]);
+      expect(datagrid.create_transaction).toHaveBeenCalledWith(
+        '/initialize/datagrid/11/view/1/2/3/5',
+        null,
+        'false',
+        'pg',
+        '',
+        'server1 - database1 - view1.view1',
+        ''
+      );
+    });
+  });
+
+  context('current node is underneath a catalog', () => {
+    it('does not create a transaction', () => {
+      showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'catalog1'}]);
+      expect(datagrid.create_transaction).toHaveBeenCalledWith(
+        '/initialize/datagrid/11/catalog/1/2/3/6',
+        null,
+        'false',
+        'pg',
+        '',
+        'server1 - database1 - catalog1.catalog1',
+        ''
+      );
+    });
+  });
+});
diff --git a/web/regression/javascript/datagrid/show_query_tool_spec.js b/web/regression/javascript/datagrid/show_query_tool_spec.js
new file mode 100644
index 00000000..66bd37ce
--- /dev/null
+++ b/web/regression/javascript/datagrid/show_query_tool_spec.js
@@ -0,0 +1,125 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {TreeFake} from '../tree/tree_fake';
+import {showQueryTool} from '../../../pgadmin/tools/datagrid/static/js/show_query_tool';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+const context = describe;
+
+describe('#showQueryTool', () => {
+  let queryTool;
+  let pgBrowser;
+  let alertify;
+  beforeEach(() => {
+    alertify = jasmine.createSpyObj('alertify', ['alert']);
+    queryTool = {
+      create_transaction: jasmine.createSpy('create_transaction'),
+    };
+    pgBrowser = {
+      treeMenu: new TreeFake(),
+      Nodes: {
+        server_group: {
+          _type: 'server_group',
+          hasId: true,
+        },
+        server: {
+          _type: 'server',
+          hasId: true,
+        },
+        database: {
+          _type: 'database',
+          hasId: true,
+        },
+      },
+    };
+    const parent = pgBrowser.treeMenu.addNewNode('parent', {_type: 'parent'});
+    const serverGroup1 =  new TreeNode('server_group1', {
+      _type: 'server_group',
+      _id: 1,
+    }, ['parent']);
+    pgBrowser.treeMenu.addChild(parent, serverGroup1);
+
+    const server1 = new TreeNode('server1', {
+      _type: 'server',
+      label: 'server1',
+      server_type: 'pg',
+      _id: 2,
+    });
+    pgBrowser.treeMenu.addChild(serverGroup1, server1);
+
+    const database1 = new  TreeNode('database1', {
+      _type: 'database',
+      label: 'database1',
+      _id: 3,
+    });
+    pgBrowser.treeMenu.addChild(server1, database1);
+  });
+
+  context('cannot find the tree node', () => {
+    beforeEach(() => {
+      showQueryTool(queryTool, pgBrowser, alertify, '', [{id: '10'}], 'title');
+    });
+    it('does not create a transaction', () => {
+      expect(queryTool.create_transaction).not.toHaveBeenCalled();
+    });
+
+    it('display alert', () => {
+      expect(alertify.alert).toHaveBeenCalledWith(
+        'Query Tool Error',
+        'No object selected.'
+      );
+    });
+  });
+
+  context('current node is not underneath a server', () => {
+    it('does not create a transaction', () => {
+      showQueryTool(queryTool, pgBrowser, alertify, '', [{id: 'parent'}], 'title');
+      expect(queryTool.create_transaction).not.toHaveBeenCalled();
+    });
+
+    it('no alert is displayed', () => {
+      expect(alertify.alert).not.toHaveBeenCalled();
+    });
+  });
+
+  context('current node is underneath a server', () => {
+    context('current node is not underneath a database', () => {
+      it('creates a transaction', () => {
+        showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'server1'}], 'title');
+        expect(queryTool.create_transaction).toHaveBeenCalledWith(
+          '/initialize/query_tool/1/2',
+          null,
+          'true',
+          'pg',
+          'http://someurl',
+          'title',
+          '',
+          false
+        );
+      });
+    });
+
+    context('current node is underneath a database', () => {
+      it('creates a transaction', () => {
+        showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'database1'}], 'title');
+        expect(queryTool.create_transaction).toHaveBeenCalledWith(
+          '/initialize/query_tool/1/2/3',
+          null,
+          'true',
+          'pg',
+          'http://someurl',
+          'title',
+          '',
+          false
+        );
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/fake_browser/browser.js b/web/regression/javascript/fake_browser/browser.js
new file mode 100644
index 00000000..195e5c51
--- /dev/null
+++ b/web/regression/javascript/fake_browser/browser.js
@@ -0,0 +1,12 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+let treeMenu = null;
+
+export {treeMenu};
diff --git a/web/regression/javascript/fake_endpoints.js b/web/regression/javascript/fake_endpoints.js
index 54b86a94..c060ba78 100644
--- a/web/regression/javascript/fake_endpoints.js
+++ b/web/regression/javascript/fake_endpoints.js
@@ -12,5 +12,11 @@ define(function () {
     'static': '/base/pgadmin/static/<path:filename>',
     'sqleditor.poll': '/sqleditor/query_tool/poll/<path:trans_id>',
     'sqleditor.query_tool_start': '/sqleditor/query_tool/start/<path:trans_id>',
+    'backup.create_server_job':  '/backup/job/<int:sid>',
+    'backup.create_object_job':  '/backup/job/<int:sid>/object',
+    'datagrid.initialize_datagrid': '/initialize/datagrid/<int:cmd_type>/<obj_type>/<int:sgid>/<int:sid>/<int:did>/<int:obj_id>',
+    'datagrid.initialize_query_tool': '/initialize/query_tool/<int:sgid>/<int:sid>',
+    'datagrid.initialize_query_tool_with_did': '/initialize/query_tool/<int:sgid>/<int:sid>/<int:did>',
+    'restore.create_job': '/restore/job/<int:sid>',
   };
 });
diff --git a/web/regression/javascript/fake_model.js b/web/regression/javascript/fake_model.js
new file mode 100644
index 00000000..acfaa532
--- /dev/null
+++ b/web/regression/javascript/fake_model.js
@@ -0,0 +1,21 @@
+export class FakeModel {
+  constructor() {
+    this.values = {};
+  }
+
+  set(key, value) {
+    this.values[key] = value;
+  }
+
+  get(key) {
+    return this.values[key];
+  }
+
+  unset(key) {
+    delete this.values[key];
+  }
+
+  toJSON() {
+    return Object.assign({}, this.values);
+  }
+}
diff --git a/web/regression/javascript/nodes/schema/child_menu_spec.js b/web/regression/javascript/nodes/schema/child_menu_spec.js
new file mode 100644
index 00000000..3d3dc55e
--- /dev/null
+++ b/web/regression/javascript/nodes/schema/child_menu_spec.js
@@ -0,0 +1,253 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+/////////////////////////////////////////////////////////////
+
+import {
+  isTreeItemOfChildOfSchema, childCreateMenuEnabled,
+} from 'pgadmin.schema.dir/schema_child_tree_node';
+
+import * as pgBrowser from 'pgbrowser/browser';
+import {TreeFake} from '../../tree/tree_fake';
+
+describe('#childCreateMenuEnabled', () => {
+  let data;
+  let tree;
+
+  describe(' - when data is not null', () => {
+    beforeEach(() => {
+      data = {};
+    });
+    describe(' and check is false', () => {
+      beforeEach(() => {
+        data = {check: false};
+      });
+      it(', then it returns true', () => {
+        expect(childCreateMenuEnabled({}, {}, data)).toBe(true);
+      });
+    });
+
+    describe(' and check', () => {
+      describe(' is true', () => {
+        beforeEach(() => {
+          data = {check: true};
+        });
+
+        describe(', on schema node', () => {
+          beforeEach(() => {
+            let hierarchy = {
+              id: 'root',
+              children: [{
+                id: 'level2',
+                data: {_type: 'schema'},
+              }],
+            };
+
+            tree = TreeFake.build(hierarchy);
+            pgBrowser.treeMenu = tree;
+          });
+          it(' it is true', () => {
+            expect(childCreateMenuEnabled(
+              {}, [{id: 'level2'}], data
+            )).toBe(true);
+
+          });
+        });
+
+        describe(', on child collection node under schema node ', () => {
+          beforeEach(() => {
+            let hierarchy = {
+              id: 'root',
+              children: [{
+                id: 'level2',
+                data: {_type: 'schema'},
+                children: [{
+                  id: 'coll-table',
+                  data: {_type: 'coll-table'},
+                }],
+              }],
+            };
+
+            tree = TreeFake.build(hierarchy);
+            pgBrowser.treeMenu = tree;
+          });
+
+          it(' it is true', () => {
+            expect(childCreateMenuEnabled(
+              {}, [{id: 'coll-table'}], data
+            )).toBe(true);
+          });
+        });
+
+        describe(', on one of the child node under schema node ', () => {
+          beforeEach(() => {
+            let hierarchy = {
+              id: 'root',
+              children: [{
+                id: 'level2',
+                data: {_type: 'schema'},
+                children: [{
+                  id: 'coll-table',
+                  data: {_type: 'coll-table'},
+                  children: [{
+                    id: 'table/1',
+                    data: {_type: 'table'},
+                  }],
+                }],
+              }],
+            };
+
+            tree = TreeFake.build(hierarchy);
+            pgBrowser.treeMenu = tree;
+          });
+
+          it(' it is true', () => {
+            expect(childCreateMenuEnabled(
+              {}, [{id: 'table/1'}], data
+            )).toBe(true);
+          });
+        });
+
+        describe(', on catalog node', () => {
+          beforeEach(() => {
+            let hierarchy = {
+              id: 'root',
+              children: [{
+                id: 'level2',
+                data: {_type: 'catalog'},
+              }],
+            };
+
+            tree = TreeFake.build(hierarchy);
+            pgBrowser.treeMenu = tree;
+          });
+          it(' it is false', () => {
+            expect(
+              childCreateMenuEnabled({}, [{id: 'level2'}], data)
+            ).toBe(false);
+          });
+        });
+
+        describe(', on child collection node under catalog node ', () => {
+          beforeEach(() => {
+            let hierarchy = {
+              id: 'root',
+              children: [{
+                id: 'level2',
+                data: {_type: 'catalog'},
+                children: [{
+                  id: 'coll-table',
+                  data: {_type: 'coll-table'},
+                }],
+              }],
+            };
+
+            tree = TreeFake.build(hierarchy);
+            pgBrowser.treeMenu = tree;
+          });
+
+          it(' it is false', () => {
+            expect(childCreateMenuEnabled(
+              {}, [{id: 'coll-table'}], data
+            )).toBe(false);
+          });
+        });
+
+        describe(', on one of the child node under catalog node ', () => {
+          beforeEach(() => {
+            let hierarchy = {
+              id: 'root',
+              children: [{
+                id: 'level2',
+                data: {_type: 'catalog'},
+                children: [{
+                  id: 'coll-table',
+                  data: {_type: 'coll-table'},
+                  children: [{
+                    id: 'table/1',
+                    data: {_type: 'table'},
+                  }],
+                }],
+              }],
+            };
+
+            tree = TreeFake.build(hierarchy);
+            pgBrowser.treeMenu = tree;
+          });
+
+          it(' it is false', () => {
+            expect(childCreateMenuEnabled(
+              {}, [{id: 'table/1'}], data
+            )).toBe(false);
+          });
+        });
+      });
+    });
+  });
+});
+
+describe('#childDropMenuEnabled', () => {
+  let tree;
+
+  describe(' - the child node under schema node ', () => {
+    beforeEach(() => {
+      let hierarchy = {
+        id: 'root',
+        children: [{
+          id: 'level2',
+          data: {_type: 'schema'},
+          children: [{
+            id: 'coll-table',
+            data: {_type: 'coll-table'},
+            children: [{
+              id: 'table/1',
+              data: {_type: 'table'},
+            }],
+          }],
+        }],
+      };
+
+      tree = TreeFake.build(hierarchy);
+      pgBrowser.treeMenu = tree;
+    });
+
+    it(' it is true', () => {
+      expect(isTreeItemOfChildOfSchema(
+        {}, [{id: 'table/1'}]
+      )).toBe(true);
+    });
+  });
+
+  describe('- the child node under the catalog node ', () => {
+    beforeEach(() => {
+      let hierarchy = {
+        id: 'root',
+        children: [{
+          id: 'level2',
+          data: {_type: 'catalog'},
+          children: [{
+            id: 'coll-table',
+            data: {_type: 'coll-table'},
+            children: [{
+              id: 'table/1',
+              data: {_type: 'table'},
+            }],
+          }],
+        }],
+      };
+
+      tree = TreeFake.build(hierarchy);
+      pgBrowser.treeMenu = tree;
+    });
+
+    it(' it is false', () => {
+      expect(isTreeItemOfChildOfSchema(
+        {}, [{id: 'table/1'}]
+      )).toBe(false);
+    });
+  });
+});
diff --git a/web/regression/javascript/restore/restore_dialog_spec.js b/web/regression/javascript/restore/restore_dialog_spec.js
new file mode 100644
index 00000000..156f56bb
--- /dev/null
+++ b/web/regression/javascript/restore/restore_dialog_spec.js
@@ -0,0 +1,203 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import {TreeFake} from '../tree/tree_fake';
+import {RestoreDialog} from '../../../pgadmin/tools/restore/static/js/restore_dialog';
+
+const context = describe;
+
+describe('RestoreDialog', () => {
+  let restoreDialog;
+  let pgBrowser;
+  let jquerySpy;
+  let alertifySpy;
+  let restoreModelSpy;
+
+  beforeEach(() => {
+    pgBrowser = {
+      treeMenu: new TreeFake(),
+      Nodes: {
+        server: jasmine.createSpyObj('Node[server]', ['getTreeNodeHierarchy']),
+        database: jasmine.createSpyObj('Node[database]', ['getTreeNodeHierarchy']),
+      },
+    };
+    pgBrowser.Nodes.server.hasId = true;
+    pgBrowser.Nodes.database.hasId = true;
+    jquerySpy = jasmine.createSpy('jquerySpy');
+    restoreModelSpy = jasmine.createSpy('restoreModelSpy');
+
+    const hierarchy = {
+      children: [
+        {
+          id: 'root',
+          children: [
+            {
+              id: 'serverTreeNode',
+              data: {
+                _id: 10,
+                _type: 'server',
+                label: 'some-tree-label',
+              },
+              children: [
+                {
+                  id: 'some_database',
+                  data: {
+                    _type: 'database',
+                    _id: 11,
+                    label: 'some_database',
+                    _label: 'some_database_label',
+                  },
+                }, {
+                  id: 'database_with_equal_in_name',
+                  data: {
+                    _type: 'database',
+                    label: 'some_database',
+                    _label: '=some_database_label',
+                  },
+                },
+              ],
+            },
+            {
+              id: 'ppasServer',
+              data: {
+                _type: 'server',
+                server_type: 'ppas',
+                children: [
+                  {id: 'someNodeUnderneathPPASServer'},
+                ],
+              },
+            },
+          ],
+        },
+      ],
+    };
+
+    pgBrowser.treeMenu = TreeFake.build(hierarchy);
+  });
+
+  describe('#draw', () => {
+    beforeEach(() => {
+      alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+      alertifySpy['pg_restore'] = jasmine.createSpy('pg_restore');
+      restoreDialog = new RestoreDialog(
+        pgBrowser,
+        jquerySpy,
+        alertifySpy,
+        restoreModelSpy
+      );
+
+      pgBrowser.get_preference = jasmine.createSpy('get_preferences');
+    });
+
+    context('there are no ancestors of the type server', () => {
+      it('does not create a dialog', () => {
+        pgBrowser.treeMenu.selectNode([{id: 'root'}]);
+        restoreDialog.draw(null, null, null);
+        expect(alertifySpy['pg_restore']).not.toHaveBeenCalled();
+      });
+
+      it('display an alert with a Restore Error', () => {
+        restoreDialog.draw(null, [{id: 'root'}], null);
+        expect(alertifySpy.alert).toHaveBeenCalledWith(
+          'Restore Error',
+          'Please select server or child node from the browser tree.'
+        );
+      });
+    });
+
+    context('there is an ancestor of the type server', () => {
+      context('no preference can be found', () => {
+        beforeEach(() => {
+          pgBrowser.get_preference.and.returnValue(undefined);
+        });
+
+        context('server is a ppas server', () => {
+          it('display an alert with "Restore Error"', () => {
+            restoreDialog.draw(null, [{id: 'serverTreeNode'}], null);
+            expect(alertifySpy.alert).toHaveBeenCalledWith(
+              'Restore Error',
+              'Failed to load preference pg_bin_dir of module paths'
+            );
+          });
+        });
+
+        context('server is not a ppas server', () => {
+          it('display an alert with "Restore Error"', () => {
+            restoreDialog.draw(null, [{id: 'ppasServer'}], null);
+            expect(alertifySpy.alert).toHaveBeenCalledWith(
+              'Restore Error',
+              'Failed to load preference ppas_bin_dir of module paths'
+            );
+          });
+        });
+      });
+
+      context('preference can be found', () => {
+        context('binary folder is not configured', () => {
+          beforeEach(() => {
+            pgBrowser.get_preference.and.returnValue({});
+          });
+
+          context('server is a ppas server', () => {
+            it('display an alert with "Configuration required"', () => {
+              restoreDialog.draw(null, [{id: 'serverTreeNode'}], null);
+              expect(alertifySpy.alert).toHaveBeenCalledWith(
+                'Configuration required',
+                'Please configure the PostgreSQL Binary Path in the Preferences dialog.'
+              );
+            });
+          });
+
+          context('server is not a ppas server', () => {
+            it('display an alert with "Configuration required"', () => {
+              restoreDialog.draw(null, [{id: 'ppasServer'}], null);
+              expect(alertifySpy.alert).toHaveBeenCalledWith(
+                'Configuration required',
+                'Please configure the EDB Advanced Server Binary Path in the Preferences dialog.'
+              );
+            });
+          });
+        });
+
+        context('binary folder is configured', () => {
+          let spy;
+          beforeEach(() => {
+            spy = jasmine.createSpyObj('globals', ['resizeTo']);
+            alertifySpy['pg_restore'].and
+              .returnValue(spy);
+            pgBrowser.get_preference.and.returnValue({value: '/some/path'});
+            pgBrowser.Nodes.server.label = 'some-server-label';
+          });
+
+          it('displays the dialog', () => {
+            restoreDialog.draw(null, [{id: 'serverTreeNode'}], {server: true});
+            expect(alertifySpy['pg_restore']).toHaveBeenCalledWith(
+              'Restore (some-server-label: some-tree-label)',
+              [{id: 'serverTreeNode'}],
+              {
+                _id: 10,
+                _type: 'server',
+                label: 'some-tree-label',
+              },
+              pgBrowser.Nodes.server
+            );
+            expect(spy.resizeTo).toHaveBeenCalledWith('65%', '60%');
+          });
+
+          context('database label contain "="', () => {
+            it('should create alert dialog with restore error', () => {
+              restoreDialog.draw(null, [{id: 'database_with_equal_in_name'}], null);
+              expect(alertifySpy.alert).toHaveBeenCalledWith('Restore Error',
+                'Databases with = symbols in the name cannot be backed up or restored using this utility.');
+            });
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/restore/restore_dialog_wrapper_spec.js b/web/regression/javascript/restore/restore_dialog_wrapper_spec.js
new file mode 100644
index 00000000..c2a31d55
--- /dev/null
+++ b/web/regression/javascript/restore/restore_dialog_wrapper_spec.js
@@ -0,0 +1,593 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import {TreeFake} from '../tree/tree_fake';
+import {RestoreDialogWrapper} from '../../../pgadmin/tools/restore/static/js/restore_dialog_wrapper';
+import MockAdapter from 'axios-mock-adapter';
+import axios from 'axios/index';
+import {FakeModel} from '../fake_model';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+let context = describe;
+
+describe('RestoreDialogWrapper', () => {
+  let jquerySpy;
+  let pgBrowser;
+  let alertifySpy;
+  let dialogModelKlassSpy;
+  let backform;
+  let generatedRestoreModel;
+  let restoreDialogWrapper;
+  let noDataNode;
+  let serverTreeNode;
+  let viewSchema;
+  let restoreJQueryContainerSpy;
+  let restoreNodeChildNodeSpy;
+  let restoreNode;
+
+  beforeEach(() => {
+    pgBrowser = {
+      treeMenu: new TreeFake(),
+      Nodes: {
+        server: {
+          hasId: true,
+          getTreeNodeHierarchy: jasmine.createSpy('getTreeNodeHierarchy'),
+        },
+      },
+      keyboardNavigation: jasmine.createSpyObj('keyboardNavigation', ['getDialogTabNavigator']),
+    };
+
+    noDataNode = pgBrowser.treeMenu.addNewNode('level1.1', undefined, [{id: 'level1'}]);
+    serverTreeNode = pgBrowser.treeMenu.addNewNode('level2.1', {
+      _type: 'server',
+      _id: 10,
+      label: 'some-tree-label',
+    }, [{id: 'level2.1'}]);
+    jquerySpy = jasmine.createSpy('jquerySpy');
+    dialogModelKlassSpy = jasmine.createSpy('dialogModelKlass');
+    generatedRestoreModel = {};
+    viewSchema = {};
+    backform = jasmine.createSpyObj('backform', ['generateViewSchema', 'Dialog']);
+    backform.generateViewSchema.and.returnValue(viewSchema);
+    dialogModelKlassSpy.and.returnValue(generatedRestoreModel);
+    restoreJQueryContainerSpy = jasmine.createSpyObj('restoreJQueryContainer', ['get', 'attr']);
+    restoreJQueryContainerSpy.get.and.returnValue(restoreJQueryContainerSpy);
+
+    restoreNode = {
+      __internal: {
+        buttons: [
+          {}, {},
+          {
+            element: {
+              disabled: false,
+            },
+          },
+        ],
+      },
+      elements: {
+        body: {
+          childNodes: [
+            {},
+          ],
+        },
+        content: jasmine.createSpyObj('content', ['appendChild', 'attr']),
+      },
+    };
+
+
+    restoreNodeChildNodeSpy = jasmine.createSpyObj('something', ['addClass']);
+
+    jquerySpy.and.callFake((selector) => {
+      if (selector === '<div class=\'restore_dialog\'></div>') {
+        return restoreJQueryContainerSpy;
+      } else if (selector === restoreNode.elements.body.childNodes[0]) {
+        return restoreNodeChildNodeSpy;
+      }
+    });
+  });
+
+  describe('#prepare', () => {
+
+    beforeEach(() => {
+      alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
+      restoreDialogWrapper = new RestoreDialogWrapper(
+        '<div class=\'restore_dialog\'></div>',
+        'restoreDialogTitle',
+        'restore',
+        jquerySpy,
+        pgBrowser,
+        alertifySpy,
+        dialogModelKlassSpy,
+        backform
+      );
+      restoreDialogWrapper = Object.assign(restoreDialogWrapper, restoreNode);
+    });
+    context('no tree element is selected', () => {
+      it('does not create a backform dialog', () => {
+        restoreDialogWrapper.prepare();
+        expect(backform.Dialog).not.toHaveBeenCalled();
+      });
+
+      it('disables the button "submit button" until a filename is selected', () => {
+        restoreDialogWrapper.prepare();
+        expect(restoreDialogWrapper.__internal.buttons[2].element.disabled).toBe(true);
+      });
+    });
+
+    context('selected tree node has no data', () => {
+      beforeEach(() => {
+        pgBrowser.treeMenu.selectNode(noDataNode.domNode);
+      });
+
+      it('does not create a backform dialog', () => {
+        restoreDialogWrapper.prepare();
+        expect(backform.Dialog).not.toHaveBeenCalled();
+      });
+
+      it('disables the button "submit button" until a filename is selected', () => {
+        restoreDialogWrapper.prepare();
+        expect(restoreDialogWrapper.__internal.buttons[2].element.disabled).toBe(true);
+      });
+    });
+
+    context('tree element is selected', () => {
+      let treeHierarchyInformation;
+      let dialogSpy;
+      beforeEach(() => {
+        treeHierarchyInformation = {
+          server: {
+            _type: 'server',
+            _id: 10,
+            priority: 0,
+            label: 'some-tree-label',
+          },
+        };
+        pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+        pgBrowser.Nodes['server'].getTreeNodeHierarchy.and
+          .returnValue(treeHierarchyInformation);
+        dialogSpy = jasmine.createSpyObj('newView', ['render']);
+        dialogSpy.$el = jasmine.createSpyObj('$el', ['find', 'attr']);
+        dialogSpy.model = jasmine.createSpyObj('model', ['on']);
+        dialogSpy.$el.find.and.returnValue([]);
+
+        backform.Dialog.and.returnValue(dialogSpy);
+      });
+
+      it('creates a backform dialog and displays it', () => {
+        restoreDialogWrapper.prepare();
+        expect(backform.Dialog).toHaveBeenCalledWith({
+          el: restoreJQueryContainerSpy,
+          model: generatedRestoreModel,
+          schema: viewSchema,
+        });
+
+        expect(dialogSpy.render).toHaveBeenCalled();
+      });
+
+      it('add alertify classes to restore node childnode', () => {
+        restoreDialogWrapper.prepare();
+        expect(restoreNodeChildNodeSpy.addClass)
+          .toHaveBeenCalledWith('alertify_tools_dialog_properties obj_properties');
+      });
+
+      it('disables the button submit button until a filename is selected', () => {
+        restoreDialogWrapper.prepare();
+        expect(restoreNode.__internal.buttons[2].element.disabled).toBe(true);
+      });
+
+      it('generates a new restore model', () => {
+        restoreDialogWrapper.prepare();
+        expect(dialogModelKlassSpy).toHaveBeenCalledWith(
+          {node_data: pgBrowser.Nodes['server']},
+          {node_info: treeHierarchyInformation}
+        );
+      });
+
+      it('add the new dialog to the restore node HTML', () => {
+        restoreDialogWrapper.prepare();
+        expect(restoreNode.elements.content.appendChild).toHaveBeenCalledWith(restoreJQueryContainerSpy);
+      });
+    });
+  });
+
+  describe('onButtonClicked', () => {
+    let networkMock;
+
+    beforeEach(() => {
+      pgBrowser.showHelp = jasmine.createSpy('showHelp');
+      networkMock = new MockAdapter(axios);
+      alertifySpy = jasmine.createSpyObj('alertify', ['success', 'alert']);
+      restoreDialogWrapper = new RestoreDialogWrapper(
+        '<div class=\'restore_dialog\'></div>',
+        'restoreDialogTitle',
+        'restore',
+        jquerySpy,
+        pgBrowser,
+        alertifySpy,
+        dialogModelKlassSpy,
+        backform
+      );
+      restoreDialogWrapper = Object.assign(restoreDialogWrapper, restoreNode);
+
+    });
+
+    afterEach(function () {
+      networkMock.restore();
+    });
+
+    context('dialog help button was pressed', () => {
+      let networkCalled;
+      beforeEach(() => {
+        networkCalled = false;
+        pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+        networkMock.onAny(/.+/).reply(() => {
+          networkCalled = true;
+          return [200, {}];
+        });
+
+        const event = {
+          button: {
+            element: {
+              name: 'dialog_help',
+              getAttribute: (attributeName) => {
+                if (attributeName === 'url') {
+                  return 'http://someurl';
+                } else if (attributeName === 'label') {
+                  return 'some label';
+                }
+              },
+            },
+          },
+        };
+        restoreDialogWrapper.callback(event);
+      });
+
+      it('displays help for dialog', () => {
+        expect(pgBrowser.showHelp).toHaveBeenCalledWith(
+          'dialog_help',
+          'http://someurl',
+          pgBrowser.Nodes['server'],
+          serverTreeNode,
+          'some label'
+        );
+      });
+
+      it('does not start the restore', () => {
+        expect(networkCalled).toBe(false);
+      });
+    });
+
+    context('object help button was pressed', () => {
+      let networkCalled;
+      beforeEach(() => {
+        networkCalled = false;
+        pgBrowser.treeMenu.selectNode(serverTreeNode.domNode);
+        networkMock.onAny(/.+/).reply(() => {
+          networkCalled = true;
+          return [200, {}];
+        });
+
+        const event = {
+          button: {
+            element: {
+              name: 'object_help',
+              getAttribute: (attributeName) => {
+                if (attributeName === 'url') {
+                  return 'http://someurl';
+                } else if (attributeName === 'label') {
+                  return 'some label';
+                }
+              },
+            },
+          },
+        };
+        restoreDialogWrapper.callback(event);
+      });
+
+      it('displays help for dialog', () => {
+        expect(pgBrowser.showHelp).toHaveBeenCalledWith(
+          'object_help',
+          'http://someurl',
+          pgBrowser.Nodes['server'],
+          serverTreeNode,
+          'some label'
+        );
+      });
+
+      it('does not start the restore', () => {
+        expect(networkCalled).toBe(false);
+      });
+    });
+
+    context('restore button was pressed', () => {
+      let networkCalled;
+      let event;
+
+      context('no tree node is selected', () => {
+        beforeEach(() => {
+          networkCalled = false;
+          networkMock.onAny(/.+/).reply(() => {
+            networkCalled = true;
+            return [200, {}];
+          });
+          event = {
+            button: {
+              'data-btn-name': 'restore',
+              element: {
+                getAttribute: () => {
+                  return 'http://someurl';
+                },
+              },
+            },
+          };
+        });
+
+        it('does not start the restore', () => {
+          restoreDialogWrapper.callback(event);
+          expect(networkCalled).toBe(false);
+        });
+      });
+
+      context('tree node selected has no data', () => {
+        beforeEach(() => {
+          networkCalled = false;
+          networkMock.onAny(/.+/).reply(() => {
+            networkCalled = true;
+            return [200, {}];
+          });
+          event = {
+            button: {
+              'data-btn-name': 'restore',
+              element: {
+                getAttribute: () => {
+                  return 'http://someurl';
+                },
+              },
+            },
+          };
+          pgBrowser.treeMenu.selectNode(noDataNode.domNode);
+        });
+
+        it('does not start the restore', () => {
+          restoreDialogWrapper.callback(event);
+          expect(networkCalled).toBe(false);
+        });
+      });
+
+      context('tree node select has data', () => {
+
+        let databaseTreeNode;
+
+        beforeEach(() => {
+          databaseTreeNode = pgBrowser.treeMenu.addNewNode('level3.1', {
+            _type: 'database',
+            _id: 10,
+            _label: 'some-database-label',
+          }, [{id: 'level3.1'}]);
+          pgBrowser.treeMenu.addChild(serverTreeNode, databaseTreeNode);
+          pgBrowser.Nodes.database = {
+            hasId: true,
+            _label: 'some-database-label',
+          };
+          let fakeModel = new FakeModel();
+          fakeModel.set('some-key', 'some-value');
+          restoreDialogWrapper.view = {
+            model: fakeModel,
+          };
+          pgBrowser.treeMenu.selectNode(databaseTreeNode.domNode);
+          pgBrowser.Events = jasmine.createSpyObj('pgBrowserEventsSpy', ['trigger']);
+          event = {
+            button: {
+              'data-btn-name': 'restore',
+              element: {
+                getAttribute: () => {
+                  return 'http://someurl';
+                },
+              },
+            },
+          };
+        });
+        context('restore job created successfully', () => {
+          let dataSentToServer;
+          beforeEach(() => {
+            networkMock.onPost('/restore/job/10').reply((request) => {
+              dataSentToServer = request.data;
+              return [200, {}];
+            });
+          });
+
+          it('create an success alert box', (done) => {
+            restoreDialogWrapper.callback(event);
+            setTimeout(() => {
+              expect(alertifySpy.success).toHaveBeenCalledWith(
+                'Restore job created.',
+                5
+              );
+              done();
+            }, 0);
+          });
+
+          it('trigger background process', (done) => {
+            restoreDialogWrapper.callback(event);
+            setTimeout(() => {
+              expect(pgBrowser.Events.trigger).toHaveBeenCalledWith(
+                'pgadmin-bgprocess:created',
+                restoreDialogWrapper
+              );
+              done();
+            }, 0);
+          });
+
+          it('send correct data to server', (done) => {
+            restoreDialogWrapper.callback(event);
+            setTimeout(() => {
+              expect(JSON.parse(dataSentToServer)).toEqual({
+                'some-key': 'some-value',
+                'database': 'some-database-label',
+              });
+              done();
+            }, 0);
+          });
+        });
+
+        context('error creating restore job', () => {
+          beforeEach(() => {
+            networkMock.onPost('/restore/job/10').reply(() => {
+              return [400, {}];
+            });
+          });
+
+          it('creates an alert box', (done) => {
+            restoreDialogWrapper.callback(event);
+            setTimeout(() => {
+              expect(alertifySpy.alert).toHaveBeenCalledWith(
+                'Restore job failed.',
+                undefined
+              );
+              done();
+            }, 0);
+          });
+        });
+      });
+    });
+  });
+
+  describe('setExtraParameters', () => {
+    let selectedNode;
+    let treeInfo;
+    let model;
+
+    beforeEach(() => {
+      restoreDialogWrapper = new RestoreDialogWrapper(
+        '<div class=\'restore_dialog\'></div>',
+        'restoreDialogTitle',
+        'restore',
+        jquerySpy,
+        pgBrowser,
+        alertifySpy,
+        dialogModelKlassSpy,
+        backform
+      );
+
+      model = new FakeModel();
+      restoreDialogWrapper.view = {
+        model: model,
+      };
+    });
+
+    context('when it is a custom model', () => {
+      beforeEach(() => {
+        model.set('custom', true);
+        treeInfo = {
+          'database': {
+            '_label': 'some-database-label',
+          },
+        };
+      });
+
+      it('only sets the database', () => {
+        restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+        expect(model.toJSON()).toEqual({
+          'custom': true,
+          'database': 'some-database-label',
+        });
+      });
+    });
+
+    context('when it is not a custom model', () => {
+      beforeEach(() => {
+        model.set('custom', false);
+        treeInfo = {
+          'database': {
+            '_label': 'some-database-label',
+          },
+          'schema': {
+            '_label': 'some-schema-label',
+          },
+        };
+      });
+
+      context('when selected node is a schema', () => {
+        it('sets schemas on the model', () => {
+          selectedNode = new TreeNode('schema', {_type: 'schema', _label: 'some-schema-label'}, '');
+          restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+          expect(model.toJSON()).toEqual({
+            custom: false,
+            database: 'some-database-label',
+            schemas: ['some-schema-label'],
+          });
+        });
+      });
+
+      context('when selected node is a table', () => {
+        it('sets schemas and table on the model', () => {
+          selectedNode = new TreeNode('table', {_type: 'table', _label: 'some-table-label'}, '');
+          restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+          expect(model.toJSON()).toEqual({
+            custom: false,
+            database: 'some-database-label',
+            schemas: ['some-schema-label'],
+            tables: ['some-table-label'],
+          });
+        });
+      });
+
+      context('when selected node is a function', () => {
+        it('sets schemas and function on the model', () => {
+          selectedNode = new TreeNode('function', {_type: 'function', _label: 'some-function-label'}, '');
+          restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+          expect(model.toJSON()).toEqual({
+            custom: false,
+            database: 'some-database-label',
+            schemas: ['some-schema-label'],
+            functions: ['some-function-label'],
+          });
+        });
+      });
+
+      context('when selected node is an index', () => {
+        it('sets schemas and index on the model', () => {
+          selectedNode = new TreeNode('index', {_type: 'index', _label: 'some-index-label'}, '');
+          restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+          expect(model.toJSON()).toEqual({
+            custom: false,
+            database: 'some-database-label',
+            schemas: ['some-schema-label'],
+            indexes: ['some-index-label'],
+          });
+        });
+      });
+
+      context('when selected node is a trigger', () => {
+        it('sets schemas and trigger on the model', () => {
+          selectedNode = new TreeNode('trigger', {_type: 'trigger', _label: 'some-trigger-label'}, '');
+          restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+          expect(model.toJSON()).toEqual({
+            custom: false,
+            database: 'some-database-label',
+            schemas: ['some-schema-label'],
+            triggers: ['some-trigger-label'],
+          });
+        });
+      });
+
+      context('when selected node is a trigger_func', () => {
+        it('sets schemas and trigger_func on the model', () => {
+          selectedNode = new TreeNode('trigger_func', {_type: 'trigger_func', _label: 'some-trigger_func-label'}, '');
+          restoreDialogWrapper.setExtraParameters(selectedNode, treeInfo);
+          expect(model.toJSON()).toEqual({
+            custom: false,
+            database: 'some-database-label',
+            schemas: ['some-schema-label'],
+            trigger_funcs: ['some-trigger_func-label'],
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/sqleditor/filter_dialog_specs.js b/web/regression/javascript/sqleditor/filter_dialog_specs.js
index ed77dff5..cea75e6b 100644
--- a/web/regression/javascript/sqleditor/filter_dialog_specs.js
+++ b/web/regression/javascript/sqleditor/filter_dialog_specs.js
@@ -7,10 +7,8 @@
 //
 //////////////////////////////////////////////////////////////////////////
 import filterDialog from 'sources/sqleditor/filter_dialog';
-// import filterDialogModel from 'sources/sqleditor/filter_dialog_model';
 
 describe('filterDialog', () => {
-  jasmine.createSpy('sqlEditorController');
   describe('filterDialog', () => {
     describe('when using filter dialog', () => {
       beforeEach(() => {
diff --git a/web/regression/javascript/table/enable_disable_triggers_spec.js b/web/regression/javascript/table/enable_disable_triggers_spec.js
new file mode 100644
index 00000000..7bdd284e
--- /dev/null
+++ b/web/regression/javascript/table/enable_disable_triggers_spec.js
@@ -0,0 +1,271 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import MockAdapter from 'axios-mock-adapter';
+import axios from 'axios/index';
+import {
+  enableTriggers,
+  disableTriggers,
+} from '../../../pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/enable_disable_triggers';
+import {TreeFake} from '../tree/tree_fake';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+
+describe('#enableTriggers', () => {
+  let networkMock;
+  let tree;
+  let alertify;
+  let generateUrlSpy;
+  beforeEach(() => {
+    networkMock = new MockAdapter(axios);
+    tree = new TreeFake();
+    const server1 = tree.addNewNode('server1', {_id: 1}, ['<li>server1</li>']);
+    const database1 = tree.addNewNode('database1', {_type: 'database'}, ['<li>database1</li>']);
+    tree.addChild(server1, database1);
+
+    const schema1 = tree.addNewNode('schema1', {_type: 'schema'}, ['<li>schema1</li>']);
+    tree.addChild(database1, schema1);
+
+    const table1 = tree.addNewNode('table1', {_type: 'table'}, ['<li>table1</li>']);
+    tree.addChild(schema1, table1);
+
+    const column1 = tree.addNewNode('column1', {_type: 'column'}, ['<li>column1</li>']);
+    tree.addChild(table1, column1);
+
+    const tableNoData = tree.addNewNode('table-no-data', undefined, ['<li>table-no-data</li>']);
+    tree.addChild(schema1, tableNoData);
+
+    alertify = jasmine.createSpyObj('alertify', ['success', 'error']);
+    generateUrlSpy = jasmine.createSpy('generateUrl');
+    generateUrlSpy.and.returnValue('/some/place');
+  });
+
+  describe('no node is selected', () => {
+    it('does not send the request to the backend', (done) => {
+      networkMock.onAny('.*').reply(200, () => {
+      });
+
+      setTimeout(() => {
+        expect(enableTriggers(tree, alertify, generateUrlSpy, {})).toBe(false);
+        done();
+      }, 0);
+    });
+  });
+
+  describe('a node is selected', () => {
+    describe('node as no data', () => {
+      it('does not send the request to the backend', () => {
+        tree.selectNode([{id: 'table-no-data'}]);
+
+        networkMock.onAny('.*').reply(200, () => {
+        });
+
+        setTimeout(() => {
+          expect(enableTriggers(tree, alertify, generateUrlSpy, {})).toBe(false);
+        }, 0);
+      });
+    });
+
+    describe('node as  data', () => {
+      describe('backend responds with success', () => {
+        let networkMockCalledWith;
+        beforeEach(() => {
+          networkMockCalledWith = false;
+          networkMock.onPut(/.*/).reply((configuration) => {
+            networkMockCalledWith = configuration;
+            return [200, {
+              success: 1,
+              info: 'some information',
+            }];
+          });
+        });
+
+        it('displays an alert box with success', (done) => {
+          tree.selectNode([{id: 'table1'}]);
+          enableTriggers(tree, alertify, generateUrlSpy, {});
+          setTimeout(() => {
+            expect(alertify.success).toHaveBeenCalledWith('some information');
+            done();
+          }, 0);
+        });
+
+        it('reloads the node', (done) => {
+          enableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+          setTimeout(() => {
+            expect(tree.selected()).toEqual(['<li>table1</li>']);
+            done();
+          }, 20);
+        });
+
+        it('call backend with the correct parameters', (done) => {
+          enableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+          setTimeout(() => {
+            expect(networkMockCalledWith.data).toEqual(JSON.stringify({enable: 'true'}));
+            done();
+          }, 0);
+        });
+      });
+
+      describe('backend responds with error', () => {
+        beforeEach(() => {
+          networkMock.onPut(/.*/).reply(() => {
+            return [500, {
+              success: 0,
+              errormsg: 'some error message',
+            }];
+          });
+        });
+
+        it('displays an error alert', (done) => {
+          tree.selectNode([{id: 'table1'}]);
+          enableTriggers(tree, alertify, generateUrlSpy, {});
+          setTimeout(() => {
+            expect(alertify.error).toHaveBeenCalledWith('some error message');
+            done();
+          }, 0);
+        });
+
+        it('unload the node', (done) => {
+          enableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+
+          setTimeout(() => {
+            expect(tree.findNodeByDomElement([{id: 'table1'}]).children.length).toBe(0);
+            done();
+          }, 20);
+        });
+      });
+    });
+  });
+});
+
+describe('#disableTriggers', () => {
+  let networkMock;
+  let tree;
+  let alertify;
+  let generateUrlSpy;
+  beforeEach(() => {
+    networkMock = new MockAdapter(axios);
+    tree = new TreeFake();
+    const server1 = tree.addNewNode('server1', {_id: 1}, ['<li>server1</li>']);
+    const database1 = new TreeNode('database1', {_type: 'database'}, ['<li>database1</li>']);
+    tree.addChild(server1, database1);
+
+    const schema1 = new TreeNode('schema1', {_type: 'schema'}, ['<li>schema1</li>']);
+    tree.addChild(database1, schema1);
+
+    const table1 = new TreeNode('table1', {_type: 'table'}, ['<li>table1</li>']);
+    tree.addChild(schema1, table1);
+
+    const column1 = new TreeNode('column1', {_type: 'column'}, ['<li>column1</li>']);
+    tree.addChild(table1, column1);
+
+    const tableNoData = new TreeNode('table-no-data', undefined, ['<li>table-no-data</li>']);
+    tree.addChild(schema1, tableNoData);
+
+    alertify = jasmine.createSpyObj('alertify', ['success', 'error']);
+    generateUrlSpy = jasmine.createSpy('generateUrl');
+    generateUrlSpy.and.returnValue('/some/place');
+  });
+
+  describe('no node is selected', () => {
+    it('does not send the request to the backend', (done) => {
+      networkMock.onAny('.*').reply(200, () => {
+      });
+
+      setTimeout(() => {
+        expect(disableTriggers(tree, alertify, generateUrlSpy, {})).toBe(false);
+        done();
+      }, 0);
+    });
+  });
+
+  describe('a node is selected', () => {
+    describe('node as no data', () => {
+      it('does not send the request to the backend', () => {
+        tree.selectNode([{id: 'table-no-data'}]);
+
+        networkMock.onAny('.*').reply(200, () => {
+        });
+
+        setTimeout(() => {
+          expect(disableTriggers(tree, alertify, generateUrlSpy, {})).toBe(false);
+        }, 0);
+      });
+    });
+
+    describe('node as  data', () => {
+      describe('backend responds with success', () => {
+        let networkMockCalledWith;
+        beforeEach(() => {
+          networkMockCalledWith = false;
+          networkMock.onPut(/.*/).reply((configuration) => {
+            networkMockCalledWith = configuration;
+            return [200, {
+              success: 1,
+              info: 'some information',
+            }];
+          });
+        });
+
+        it('displays an alert box with success', (done) => {
+          tree.selectNode([{id: 'table1'}]);
+          disableTriggers(tree, alertify, generateUrlSpy, {});
+          setTimeout(() => {
+            expect(alertify.success).toHaveBeenCalledWith('some information');
+            done();
+          }, 0);
+        });
+
+        it('reloads the node', (done) => {
+          disableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+          setTimeout(() => {
+            expect(tree.selected()).toEqual(['<li>table1</li>']);
+            done();
+          }, 20);
+        });
+
+        it('call backend with the correct parameters', (done) => {
+          disableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+          setTimeout(() => {
+            expect(networkMockCalledWith.data).toEqual(JSON.stringify({enable: 'false'}));
+            done();
+          }, 0);
+        });
+      });
+
+      describe('backend responds with error', () => {
+        beforeEach(() => {
+          networkMock.onPut(/.*/).reply(() => {
+            return [500, {
+              success: 0,
+              errormsg: 'some error message',
+            }];
+          });
+        });
+
+        it('displays an error alert', (done) => {
+          tree.selectNode([{id: 'table1'}]);
+          disableTriggers(tree, alertify, generateUrlSpy, {});
+          setTimeout(() => {
+            expect(alertify.error).toHaveBeenCalledWith('some error message');
+            done();
+          }, 0);
+        });
+
+        it('unload the node', (done) => {
+          disableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
+
+          setTimeout(() => {
+            expect(tree.findNodeByDomElement([{id: 'table1'}]).children.length).toBe(0);
+            done();
+          }, 20);
+        });
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/tree/pgadmin_tree_node_spec.js b/web/regression/javascript/tree/pgadmin_tree_node_spec.js
new file mode 100644
index 00000000..479e515c
--- /dev/null
+++ b/web/regression/javascript/tree/pgadmin_tree_node_spec.js
@@ -0,0 +1,353 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import {
+  getTreeNodeHierarchyFromElement,
+  getTreeNodeHierarchyFromIdentifier,
+} from '../../../pgadmin/static/js/tree/pgadmin_tree_node';
+import {TreeNode} from '../../../pgadmin/static/js/tree/tree';
+import {TreeFake} from './tree_fake';
+
+const context = describe;
+
+describe('tree#node#getTreeNodeHierarchy', () => {
+  let browser;
+  let newTree;
+  beforeEach(() => {
+    newTree = new TreeFake();
+    browser = {
+      Nodes: {
+        'special one': {hasId: true},
+        'child special': {hasId: true},
+        'other type': {hasId: true},
+        'table': {hasId: true},
+        'partition': {hasId: true},
+        'no id': {hasId: false},
+      },
+    };
+    browser.treeMenu = newTree;
+  });
+
+  context('getTreeNodeHierarchy is called with aciTreeNode object', () => {
+    describe('When the current node is root element', () => {
+      beforeEach(() => {
+        newTree.addNewNode('root', {
+          'some key': 'some value',
+          '_type': 'special one',
+        });
+      });
+
+      it('returns a object with the element type passed data and priority == 0', () => {
+        const result = getTreeNodeHierarchyFromIdentifier.bind(browser)([{id: 'root'}]);
+        expect(result).toEqual({
+          'special one': {
+            'some key': 'some value',
+            '_type': 'special one',
+            'priority': 0,
+          },
+        });
+      });
+    });
+
+    describe('When the current node is not of a known type', () => {
+      beforeEach(() => {
+        newTree.addNewNode('root', {
+          'some key': 'some value',
+          '_type': 'do not exist',
+        }, []);
+      });
+
+      it('returns a empty object', () => {
+        const result = getTreeNodeHierarchyFromIdentifier.bind(browser)('root');
+        expect(result).toEqual({});
+      });
+    });
+
+    describe('When the current node type has no id', () => {
+      beforeEach(() => {
+        newTree.addNewNode('root', {
+          'some key': 'some value',
+          '_type': 'no id',
+        }, []);
+      });
+
+      it('returns a empty object', () => {
+        const result = getTreeNodeHierarchyFromIdentifier.bind(browser)('root');
+        expect(result).toEqual({});
+      });
+    });
+
+    describe('When the current node is at the second level', () => {
+      beforeEach(() => {
+        const root = newTree.addNewNode('root', {
+          'some key': 'some value',
+          '_type': 'special one',
+        });
+        const firstChild = new TreeNode('first child', {
+          'some key': 'some other value',
+          '_type': 'child special',
+        }, ['root']);
+        newTree.addChild(root, firstChild);
+      });
+
+      it('returns a empty object', () => {
+        const result = getTreeNodeHierarchyFromIdentifier.bind(browser)([{id: 'first child'}]);
+        expect(result).toEqual({
+          'child special': {
+            'some key': 'some other value',
+            '_type': 'child special',
+            'priority': 0,
+          },
+          'special one': {
+            'some key': 'some value',
+            '_type': 'special one',
+            'priority': -1,
+          },
+        });
+      });
+    });
+
+    context('When tree as "special type"', () => {
+      context('When "special type" have "other type"', () => {
+        context('When "other type" have "special type"', () => {
+          describe('When retrieving lead node', () => {
+            it('does not override previous node type data', () => {
+              const rootNode = newTree.addNewNode('root', {
+                'some key': 'some value',
+                '_type': 'special one',
+              }, []);
+
+              const level1 = new TreeNode('level 1', {
+                'some key': 'some value',
+                '_type': 'other type',
+              });
+              newTree.addChild(rootNode, level1);
+
+              newTree.addChild(level1, new TreeNode('level 2', {
+                'some key': 'expected value',
+                '_type': 'special one',
+                'some other key': 'some other value',
+              }));
+
+              const result = getTreeNodeHierarchyFromIdentifier.bind(browser)([{id: 'level 2'}]);
+              expect(result).toEqual({
+                'special one': {
+                  'some key': 'expected value',
+                  '_type': 'special one',
+                  'some other key': 'some other value',
+                  'priority': 0,
+                },
+                'other type': {
+                  'some key': 'some value',
+                  '_type': 'other type',
+                  'priority': -1,
+                },
+              });
+            });
+          });
+        });
+      });
+    });
+
+    context('When tree has table', () => {
+      context('when table has partition', () => {
+        it('returns table with partition parameters', () => {
+          const root = newTree.addNewNode('root', {
+            'some key': 'some value',
+            '_type': 'special one',
+          }, []);
+          const level1 = new TreeNode('level 1', {
+            'some key': 'some value',
+            '_type': 'table',
+          });
+          newTree.addChild(root, level1);
+          newTree.addChild(level1, new TreeNode('level 2', {
+            'some key': 'expected value',
+            '_type': 'partition',
+            'some other key': 'some other value',
+          }));
+
+          const result = getTreeNodeHierarchyFromIdentifier.bind(browser)([{id:'level 2'}]);
+          expect(result).toEqual({
+            'special one': {
+              'some key': 'some value',
+              '_type': 'special one',
+              'priority': -1,
+            },
+            'table': {
+              'some key': 'expected value',
+              'some other key': 'some other value',
+              '_type': 'partition',
+              'priority': 0,
+            },
+          });
+        });
+      });
+    });
+  });
+
+  context('getTreeNodeHierarchy is called with TreeNode object', () => {
+    let treeNode;
+    describe('When the current node is root element', () => {
+      beforeEach(() => {
+        treeNode = newTree.addNewNode('root', {
+          'some key': 'some value',
+          '_type': 'special one',
+        }, []);
+      });
+
+      it('returns a object with the element type passed data and priority == 0', () => {
+        const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+        expect(result).toEqual({
+          'special one': {
+            'some key': 'some value',
+            '_type': 'special one',
+            'priority': 0,
+          },
+        });
+      });
+    });
+
+    describe('When the current node is not of a known type', () => {
+      beforeEach(() => {
+        treeNode = newTree.addNewNode('root', {
+          'some key': 'some value',
+          '_type': 'do not exist',
+        }, []);
+      });
+
+      it('returns a empty object', () => {
+        const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+        expect(result).toEqual({});
+      });
+    });
+
+    describe('When the current node type has no id', () => {
+      beforeEach(() => {
+        treeNode = newTree.addNewNode('root', {
+          'some key': 'some value',
+          '_type': 'no id',
+        }, []);
+      });
+
+      it('returns a empty object', () => {
+        const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+        expect(result).toEqual({});
+      });
+    });
+
+    describe('When the current node is at the second level', () => {
+      beforeEach(() => {
+        const root = newTree.addNewNode('root', {
+          'some key': 'some value',
+          '_type': 'special one',
+        }, []);
+        treeNode = new TreeNode('first child', {
+          'some key': 'some other value',
+          '_type': 'child special',
+        });
+        newTree.addChild(root, treeNode);
+      });
+
+      it('returns a empty object', () => {
+        const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+        expect(result).toEqual({
+          'child special': {
+            'some key': 'some other value',
+            '_type': 'child special',
+            'priority': 0,
+          },
+          'special one': {
+            'some key': 'some value',
+            '_type': 'special one',
+            'priority': -1,
+          },
+        });
+      });
+    });
+
+    context('When tree as "special type"', () => {
+      context('When "special type" have "other type"', () => {
+        context('When "other type" have "special type"', () => {
+          describe('When retrieving lead node', () => {
+            it('does not override previous node type data', () => {
+              const root = newTree.addNewNode('root', {
+                'some key': 'some value',
+                '_type': 'special one',
+              }, []);
+              const level1 = new TreeNode('level 1', {
+                'some key': 'some value',
+                '_type': 'other type',
+              });
+              newTree.addChild(root, level1);
+              treeNode = new TreeNode('level 2', {
+                'some key': 'expected value',
+                '_type': 'special one',
+                'some other key': 'some other value',
+              });
+              newTree.addChild(level1, treeNode);
+
+              const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+              expect(result).toEqual({
+                'special one': {
+                  'some key': 'expected value',
+                  '_type': 'special one',
+                  'some other key': 'some other value',
+                  'priority': 0,
+                },
+                'other type': {
+                  'some key': 'some value',
+                  '_type': 'other type',
+                  'priority': -1,
+                },
+              });
+            });
+          });
+        });
+      });
+    });
+
+    context('When tree has table', () => {
+      context('when table has partition', () => {
+        it('returns table with partition parameters', () => {
+          const root = newTree.addNewNode('root', {
+            'some key': 'some value',
+            '_type': 'special one',
+          });
+          const level1 = newTree.addNewNode('level 1', {
+            'some key': 'some value',
+            '_type': 'table',
+          });
+          newTree.addChild(root, level1);
+          treeNode = new TreeNode('level 2', {
+            'some key': 'expected value',
+            '_type': 'partition',
+            'some other key': 'some other value',
+          });
+          newTree.addChild(level1, treeNode);
+
+          const result = getTreeNodeHierarchyFromElement(browser, treeNode);
+          expect(result).toEqual({
+            'special one': {
+              'some key': 'some value',
+              '_type': 'special one',
+              'priority': -1,
+            },
+            'table': {
+              'some key': 'expected value',
+              'some other key': 'some other value',
+              '_type': 'partition',
+              'priority': 0,
+            },
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/tree/tree_fake.js b/web/regression/javascript/tree/tree_fake.js
index b285a45f..e03d71fb 100644
--- a/web/regression/javascript/tree/tree_fake.js
+++ b/web/regression/javascript/tree/tree_fake.js
@@ -10,6 +10,32 @@
 import {Tree} from '../../../pgadmin/static/js/tree/tree';
 
 export class TreeFake extends Tree {
+  static build(structure) {
+    let tree = new TreeFake();
+    let rootNode = tree.rootNode;
+
+    if (structure.children !== undefined) {
+      structure.children.forEach((child) => {
+        TreeFake.recursivelyAddNodes(tree, child, rootNode);
+      });
+    }
+
+    return tree;
+  }
+
+  static recursivelyAddNodes(tree, newNode, parent) {
+    let id = newNode.id;
+    let data = newNode.data ? newNode.data : {};
+    let domNode = newNode.domNode ? newNode.domNode : [{id: id}];
+    tree.addNewNode(id, data, domNode, tree.translateTreeNodeIdFromACITree([parent]));
+
+    if (newNode.children !== undefined) {
+      newNode.children.forEach((child) => {
+        TreeFake.recursivelyAddNodes(tree, child, newNode);
+      });
+    }
+  }
+
   constructor() {
     super();
     this.aciTreeToOurTreeTranslator = {};
@@ -45,7 +71,7 @@ export class TreeFake extends Tree {
   }
 
   translateTreeNodeIdFromACITree(aciTreeNode) {
-    if(aciTreeNode === undefined || aciTreeNode[0] === undefined) {
+    if (aciTreeNode === undefined || aciTreeNode[0] === undefined) {
       return null;
     }
     return this.aciTreeToOurTreeTranslator[aciTreeNode[0].id];
diff --git a/web/webpack.shim.js b/web/webpack.shim.js
index 64f24336..c12e7f3b 100644
--- a/web/webpack.shim.js
+++ b/web/webpack.shim.js
@@ -130,6 +130,7 @@ var webpackShimConfig = {
     'sources/utils': path.join(__dirname, './pgadmin/static/js/utils'),
     'babel-polyfill': path.join(__dirname, './node_modules/babel-polyfill/dist/polyfill'),
     'tools': path.join(__dirname, './pgadmin/tools/'),
+    'pgbrowser': path.join(__dirname, './pgadmin/browser/static/js/'),
 
     // Vendor JS
     'jquery': path.join(__dirname, './node_modules/jquery/dist/jquery'),
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
index a1fc6824..ef893e0b 100644
--- a/web/webpack.test.config.js
+++ b/web/webpack.test.config.js
@@ -81,6 +81,8 @@ module.exports = {
       'pgadmin.alertifyjs': sourcesDir + '/js/alertify.pgadmin.defaults',
       'pgadmin.backgrid': sourcesDir + '/js/backgrid.pgadmin',
       'pgadmin.backform': sourcesDir + '/js/backform.pgadmin',
+      'pgbrowser': path.resolve(__dirname, 'regression/javascript/fake_browser'),
+      'pgadmin.schema.dir': path.resolve(__dirname, 'pgadmin/browser/server_groups/servers/databases/schemas/static/js'),
     },
   },
 };


view thread (69+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
  Subject: Re: [pgadmin4][patch] Initial patch to decouple from ACI Tree
  In-Reply-To: <CAG7mmox1C7XM23wKhhG0ok8wkKhh2QHy1EsNrmKJS3eR29KCMg@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