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..5a3a4bc0 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/js/collation.js @@ -1,8 +1,9 @@ define('pgadmin.node.collation', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin', 'pgadmin.browser', + 'sources/menu/can_create', 'pgadmin.browser.collection', -], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser) { +], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, canCreate) { if (!pgBrowser.Nodes['coll-collation']) { pgAdmin.Browser.Nodes['coll-collation'] = @@ -223,32 +224,7 @@ define('pgadmin.node.collation', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-collation', item, data); }, }); 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..1a38b083 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/js/domain.js @@ -2,9 +2,11 @@ define('pgadmin.node.domain', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid', + 'sources/menu/can_create', 'pgadmin.browser.collection', ], function( - gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid + gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid, + canCreate ) { // Define Domain Collection Node @@ -297,32 +299,7 @@ define('pgadmin.node.domain', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-domain', item, data); }, isDisabled: function(m){ if (!m.isNew()) { 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..0e820343 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js @@ -2,9 +2,11 @@ define('pgadmin.node.foreign_table', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid', + 'sources/menu/can_create', 'pgadmin.browser.collection', ], function( - gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid + gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid, + canCreate ) { if (!pgBrowser.Nodes['coll-foreign_table']) { @@ -660,32 +662,7 @@ define('pgadmin.node.foreign_table', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-foreign_table', item, data); }, }); 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..f2fe85be 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/static/js/fts_configuration.js @@ -1,9 +1,11 @@ define('pgadmin.node.fts_configuration', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid', + 'sources/menu/can_create', 'pgadmin.browser.collection', ], function( - gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid + gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, Backgrid, + canCreate ) { // Model for tokens control @@ -578,32 +580,7 @@ define('pgadmin.node.fts_configuration', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-fts_configuration', item, data); }, }); } 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..f1a330f7 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/static/js/fts_dictionary.js @@ -1,8 +1,10 @@ define('pgadmin.node.fts_dictionary', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', + 'sources/menu/can_create', 'pgadmin.browser.collection', -], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform) { +], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, + canCreate) { // Extend the browser's node model class to create a option/value pair var OptionLabelModel = pgAdmin.Browser.Node.Model.extend({ @@ -187,32 +189,7 @@ define('pgadmin.node.fts_dictionary', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-fts_dictionary', item, data); }, }); } 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..daea35de 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parser/static/js/fts_parser.js @@ -1,7 +1,9 @@ define('pgadmin.node.fts_parser', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', - 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.browser.collection', -], function(gettext, url_for, $, _, pgAdmin, pgBrowser) { + 'sources/pgadmin', 'pgadmin.browser', + 'sources/menu/can_create', + 'pgadmin.browser.collection', +], function(gettext, url_for, $, _, pgAdmin, pgBrowser, canCreate) { // Extend the collection class for fts parser if (!pgBrowser.Nodes['coll-fts_parser']) { @@ -200,32 +202,7 @@ define('pgadmin.node.fts_parser', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-fts_parser', item, data); }, }); } 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..0c008c83 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/js/fts_template.js @@ -1,7 +1,9 @@ define('pgadmin.node.fts_template', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', - 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.browser.collection', -], function(gettext, url_for, $, _, pgAdmin, pgBrowser) { + 'sources/pgadmin', 'pgadmin.browser', + 'sources/menu/can_create', + 'pgadmin.browser.collection', +], function(gettext, url_for, $, _, pgAdmin, pgBrowser, canCreate) { // Extend the collection class for fts template if (!pgBrowser.Nodes['coll-fts_template']) { @@ -140,32 +142,7 @@ define('pgadmin.node.fts_template', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-fts_template', item, data); }, }); } 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..40636446 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js @@ -2,8 +2,10 @@ define('pgadmin.node.function', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', + 'sources/menu/can_create', 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', -], function(gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform) { +], function(gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser, Backform, + canCreate) { if (!pgBrowser.Nodes['coll-function']) { pgBrowser.Nodes['coll-function'] = @@ -439,32 +441,7 @@ define('pgadmin.node.function', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-function', item, data); }, }); 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..11b587ed 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,9 @@ define('pgadmin.node.trigger_function', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', + 'sources/menu/can_create', 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', -], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform) { +], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, canCreate) { if (!pgBrowser.Nodes['coll-trigger_function']) { pgBrowser.Nodes['coll-trigger_function'] = @@ -358,32 +359,7 @@ define('pgadmin.node.trigger_function', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-trigger_function', item, data); }, }); 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..0a59efb0 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js @@ -1,8 +1,10 @@ define('pgadmin.node.sequence', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', + 'sources/menu/can_create', 'pgadmin.browser.collection', -], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform) { +], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, Backform, + canCreate) { // Extend the browser's collection class for sequence collection if (!pgBrowser.Nodes['coll-sequence']) { @@ -61,32 +63,7 @@ define('pgadmin.node.sequence', [ canDrop: pgBrowser.Nodes['schema'].canChildDrop, canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, canCreate: function(itemData, item, data) { - //If check is false then , we will allow create menu - if (data && data.check == false) - return true; - - var t = pgBrowser.tree, i = item, d = itemData; - // To iterate over tree to check parent node - while (i) { - // If it is schema then allow user to create collation - if (_.indexOf(['schema'], d._type) > -1) - return true; - - if ('coll-sequence' == d._type) { - //Check if we are not child of catalog - var prev_i = t.hasParent(i) ? t.parent(i) : null, - prev_d = prev_i ? t.itemData(prev_i) : null; - if( prev_d._type == 'catalog') { - return false; - } else { - return true; - } - } - i = t.hasParent(i) ? t.parent(i) : null; - d = i ? t.itemData(i) : null; - } - // by default we want to allow create menu - return true; + return canCreate.canCreate(pgBrowser, 'coll-sequence', item, data); }, // Define the model for sequence node. model: pgBrowser.Node.Model.extend({ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/can_drop_child.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/can_drop_child.js new file mode 100644 index 00000000..84e25b5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/can_drop_child.js @@ -0,0 +1,18 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +export function canDropChild(pgBrowser, itemData, item) { + let node = pgBrowser.treeMenu.findNodeByDomElement(item); + + if (node.anyParent((parent) => parent.getData()._type === 'catalog')) { + return false; + } + + return true; +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js index a7fd4c7c..a09610ad 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/js/schema.js @@ -1,8 +1,10 @@ define('pgadmin.node.schema', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', 'pgadmin.backgrid', + 'pgadmin.node.schema.dir/can_drop_child', 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege', -], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid) { +], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid, +canDropChild) { // VacuumSettings Collection to display all settings parameters as Grid Backform.VacuumCollectionControl = @@ -428,51 +430,12 @@ define('pgadmin.node.schema', [ // This function will checks whether we can allow user to // drop object or not based on location within schema & catalog canChildDrop: function(itemData, item) { - var t = pgBrowser.tree, i = item, d = itemData; - // To iterate over tree to check parent node - while (i) { - // If it is schema then allow user to create collation - if (_.indexOf(['schema'], d._type) > -1) - return true; - - //Check if we are not child of catalog - var prev_i = t.hasParent(i) ? t.parent(i) : null, - prev_d = prev_i ? t.itemData(prev_i) : null; - if(prev_d && prev_d._type == 'catalog') { - return false; - } - i = t.hasParent(i) ? t.parent(i) : null; - d = i ? t.itemData(i) : null; - } - // by default we do not want to allow create menu - return true; + return canDropChild.canDropChild(pgBrowser, itemData, item); }, }); pgBrowser.tableChildTreeNodeHierarchy = function(i) { - var idx = 0, - res = {}, - t = pgBrowser.tree; - - do { - var d = t.itemData(i); - if ( - d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId - ) { - if (d._type === 'partition' || d._type === 'table') { - if (!('table' in res)) { - res['table'] = _.extend({}, d, {'priority': idx}); - idx -= 1; - } - } else { - res[d._type] = _.extend({}, d, {'priority': idx}); - idx -= 1; - } - } - i = t.hasParent(i) ? t.parent(i) : null; - } while (i); - - return res; + return this.getTreeNodeHierarchy(i); }; } diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js index 72b6eb0c..1fdacf52 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js @@ -88,7 +88,6 @@ define('pgadmin.node.column', [ if (!pgBrowser.Nodes['column']) { pgBrowser.Nodes['column'] = pgBrowser.Node.extend({ - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, parent_type: ['table', 'view', 'mview'], collection_type: ['coll-table', 'coll-view', 'coll-mview'], type: 'column', diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js index 857cf4c4..ab28a86b 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint.js @@ -8,7 +8,6 @@ define('pgadmin.node.check_constraint', [ // Check Constraint Node if (!pgBrowser.Nodes['check_constraint']) { pgAdmin.Browser.Nodes['check_constraint'] = pgBrowser.Node.extend({ - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, type: 'check_constraint', label: gettext('Check'), collection_type: 'coll-constraints', diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js index 0bbf66a1..adccf2e9 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.js @@ -605,7 +605,6 @@ define('pgadmin.node.exclusion_constraint', [ // Extend the browser's node class for exclusion constraint node if (!pgBrowser.Nodes['exclusion_constraint']) { pgAdmin.Browser.Nodes['exclusion_constraint'] = pgBrowser.Node.extend({ - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, type: 'exclusion_constraint', label: gettext('Exclusion constraint'), collection_type: 'coll-constraints', diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js index 3c4b89f3..9899df92 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js @@ -603,7 +603,6 @@ define('pgadmin.node.foreign_key', [ // Extend the browser's node class for foreign key node if (!pgBrowser.Nodes['foreign_key']) { pgAdmin.Browser.Nodes['foreign_key'] = pgBrowser.Node.extend({ - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, type: 'foreign_key', label: gettext('Foreign key'), collection_type: 'coll-constraints', diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js index d3a6cff4..0ad0f054 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js @@ -20,7 +20,6 @@ define('pgadmin.node.primary_key', [ parent_type: ['table','partition'], canDrop: true, canDropCascade: true, - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, Init: function() { /* Avoid multiple registration of menus */ if (this.initialized) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js index 769185d6..18d3ca33 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js @@ -20,7 +20,6 @@ define('pgadmin.node.unique_constraint', [ parent_type: ['table','partition'], canDrop: true, canDropCascade: true, - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, Init: function() { /* Avoid multiple registration of menus */ if (this.initialized) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js index cb242cd2..9c0e24fe 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/static/js/constraints.js @@ -12,14 +12,12 @@ define('pgadmin.node.constraints', [ node: 'constraints', label: gettext('Constraints'), type: 'coll-constraints', - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, columns: ['name', 'comment'], }); } if (!pgBrowser.Nodes['constraints']) { pgAdmin.Browser.Nodes['constraints'] = pgBrowser.Node.extend({ - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, type: 'constraints', label: gettext('Constraints'), collection_type: 'coll-constraints', diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js index ec2b4da1..2b687c1a 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js @@ -13,7 +13,6 @@ define('pgadmin.node.index', [ node: 'index', label: gettext('Indexes'), type: 'coll-index', - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, sqlAlterHelp: 'sql-alterindex.html', sqlCreateHelp: 'sql-createindex.html', dialogHelp: url_for('help.static', {'filename': 'index_dialog.html'}), @@ -215,7 +214,6 @@ define('pgadmin.node.index', [ if (!pgBrowser.Nodes['index']) { pgAdmin.Browser.Nodes['index'] = pgBrowser.Node.extend({ - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, parent_type: ['table', 'view', 'mview', 'partition'], collection_type: ['coll-table', 'coll-view'], sqlAlterHelp: 'sql-alterindex.html', diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js index d807304e..c7a7819a 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 @@ -1,11 +1,15 @@ define([ + 'sources/tree/pgadmin_tree_node', 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.alertifyjs', 'pgadmin.backform', 'pgadmin.backgrid', + 'sources/menu/can_create', 'pgadmin.browser.collection', 'pgadmin.browser.table.partition.utils', ], function( - gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, Backform, Backgrid + pgadminTreeNode, + gettext, url_for, $, _, S, pgAdmin, pgBrowser, Alertify, Backform, Backgrid, + canCreate ) { if (!pgBrowser.Nodes['coll-partition']) { @@ -13,7 +17,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 +83,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', @@ -1190,32 +1163,7 @@ function( }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-table', item, data); }, // Check to whether table has disable trigger(s) canCreate_with_trigger_enable: function(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..9a021745 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,17 @@ define('pgadmin.node.table', [ + 'pgadmin.tables.js/enable_disable_triggers', 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.alertifyjs', 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.tables.js/show_advanced_tab', + 'sources/menu/can_create', + '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, canCreate ) { if (!pgBrowser.Nodes['coll-table']) { @@ -26,7 +30,6 @@ define('pgadmin.node.table', [ if (!pgBrowser.Nodes['table']) { pgBrowser.Nodes['table'] = pgBrowser.Node.extend({ - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, type: 'table', label: gettext('Table'), collection_type: 'coll-table', @@ -118,46 +121,21 @@ define('pgadmin.node.table', [ callbacks: { /* Enable trigger(s) on table */ enable_triggers_on_table: function(args) { - var params = {'enable': true }; - this.callbacks.set_triggers.apply(this, [args, params]); + tableFunctions.enableTriggers( + pgBrowser.treeMenu, + Alertify, + this.generate_url.bind(this), + args + ); }, /* Disable trigger(s) on table */ disable_triggers_on_table: function(args) { - var params = {'enable': false }; - this.callbacks.set_triggers.apply(this, [args, params]); - }, - set_triggers: function(args, params) { - // This function will send request to enable or - // disable triggers on table level - var input = args || {}, - obj = this, - t = pgBrowser.tree, - i = input.item || t.selected(), - d = i && i.length == 1 ? t.itemData(i) : undefined; - if (!d) - return false; - - $.ajax({ - url: obj.generate_url(i, 'set_trigger' , d, true), - type:'PUT', - data: params, - dataType: 'json', - success: function(res) { - if (res.success == 1) { - Alertify.success(res.info); - t.unload(i); - t.setInode(i); - t.deselect(i); - setTimeout(function() { - t.select(i); - }, 10); - } - }, - error: function(xhr, status, error) { - Alertify.pgRespErrorNotify(xhr, error); - t.unload(i); - }, - }); + tableFunctions.disableTriggers( + pgBrowser.treeMenu, + Alertify, + this.generate_url.bind(this), + args + ); }, /* Truncate table */ truncate_table: function(args) { @@ -1300,32 +1278,7 @@ define('pgadmin.node.table', [ }, }), 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; + return canCreate.canCreate(pgBrowser, 'coll-table', item, data); }, // Check to whether table has disable trigger(s) canCreate_with_trigger_enable: function(itemData, item, data) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js index a2c27188..4c25e3ea 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js @@ -29,14 +29,12 @@ define('pgadmin.node.trigger', [ node: 'trigger', label: gettext('Triggers'), type: 'coll-trigger', - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, columns: ['name', 'description'], }); } if (!pgBrowser.Nodes['trigger']) { pgAdmin.Browser.Nodes['trigger'] = pgBrowser.Node.extend({ - getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy, parent_type: ['table', 'view', 'partition'], collection_type: ['coll-table', 'coll-view'], type: 'trigger', diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js index c1c24861..91ce615f 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js @@ -1,8 +1,9 @@ define('pgadmin.node.type', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', - 'pgadmin.backgrid', 'pgadmin.browser.collection', -], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid) { + 'pgadmin.backgrid', 'sources/menu/can_create', 'pgadmin.browser.collection', +], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, Backgrid, + canCreate) { if (!pgBrowser.Nodes['coll-type']) { pgBrowser.Nodes['coll-type'] = @@ -912,32 +913,7 @@ define('pgadmin.node.type', [ }, }), 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 canCreate.canCreate(pgBrowser, 'coll-type', item, data); }, }); } 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..5791cdcd 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,11 @@ define('pgadmin.node.mview', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin', 'pgadmin.alertifyjs', 'pgadmin.browser', - 'pgadmin.backform', 'pgadmin.browser.server.privilege', -], function(gettext, url_for, $, _, pgAdmin, Alertify, pgBrowser, Backform) { + 'pgadmin.backform', + 'sources/menu/can_create', + 'pgadmin.browser.server.privilege', +], function(gettext, url_for, $, _, pgAdmin, Alertify, pgBrowser, Backform, + canCreate) { /** Create and add a view collection into nodes @@ -241,37 +244,7 @@ define('pgadmin.node.mview', [ 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; + return canCreate.canCreate(pgBrowser, 'coll-mview', item, data); }, refresh_mview: function(args) { var input = args || {}, 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..e4258188 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', + 'pgadmin.browser', 'pgadmin.backform', + 'sources/menu/can_create', + 'pgadmin.browser.server.privilege', 'pgadmin.node.rule', -], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform) { +], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, + canCreate) { /** Create and add a view collection into nodes @@ -203,38 +206,7 @@ define('pgadmin.node.view', [ 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; - + return canCreate.canCreate(pgBrowser, 'coll-view', item, data); }, }); } diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js index ad582483..3baf554a 100644 --- a/web/pgadmin/browser/static/js/node.js +++ b/web/pgadmin/browser/static/js/node.js @@ -1,9 +1,12 @@ define('pgadmin.browser.node', [ + 'sources/tree/pgadmin_tree_node', 'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin', 'pgadmin.browser.menu', 'backbone', 'pgadmin.alertifyjs', 'pgadmin.browser.datamodel', 'backform', 'sources/browser/generate_url', 'sources/utils', 'pgadmin.browser.utils', 'pgadmin.backform', -], function(gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl, commonUtils) { +], function( + pgadminTreeNode, + gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl, commonUtils) { var wcDocker = window.wcDocker, keyCode = { @@ -1566,7 +1569,6 @@ define('pgadmin.browser.node', [ * depends, statistics */ generate_url: function(item, type, d, with_id, info) { - var opURL = { 'create': 'obj', 'drop': 'obj', @@ -1608,24 +1610,7 @@ define('pgadmin.browser.node', [ Collection: pgBrowser.DataCollection, // Base class for Node Data Model Model: pgBrowser.DataModel, - getTreeNodeHierarchy: function(i) { - var idx = 0, - res = {}, - t = pgBrowser.tree, - d; - do { - d = t.itemData(i); - if (d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId) { - res[d._type] = _.extend({}, d, { - 'priority': idx, - }); - idx -= 1; - } - i = t.hasParent(i) ? t.parent(i) : null; - } while (i); - - return res; - }, + getTreeNodeHierarchy: pgadminTreeNode.getTreeNodeHierarchyFromIdentifier.bind(pgBrowser), cache: function(url, node_info, level, data) { var cached = this.cached = this.cached || {}, hash = url, diff --git a/web/pgadmin/static/js/alertify/dialog.js b/web/pgadmin/static/js/alertify/dialog.js new file mode 100644 index 00000000..5a8646f9 --- /dev/null +++ b/web/pgadmin/static/js/alertify/dialog.js @@ -0,0 +1,129 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import gettext from '../gettext'; +import {sprintf} from 'sprintf-js'; +import {DialogFactory} from './dialog_factory'; +import Backform from '../backform.pgadmin'; +import {getTreeNodeHierarchyFromIdentifier} from '../tree/pgadmin_tree_node'; + +/** + * This class can be extended to create new dialog boxes. + * Examples of this can be found in: + * `web/pgadmin/static/js/backup/backup_dialog.js` + * + * Do not forget to add the new Dialog type to the `DialogFactory` + */ +export class Dialog { + constructor(errorAlertTitle, + dialogContainerSelector, + pgBrowser, $, alertify, DialogModel, + backform = Backform) { + this.errorAlertTitle = errorAlertTitle; + this.alertify = alertify; + this.pgBrowser = pgBrowser; + this.jquery = $; + this.dialogModel = DialogModel; + this.backform = backform; + this.dialogContainerSelector = dialogContainerSelector; + } + + retrieveAncestorOfTypeServer(item) { + let serverInformation = null; + let aciTreeItem = item || this.pgBrowser.treeMenu.selected(); + let treeNode = this.pgBrowser.treeMenu.findNodeByDomElement(aciTreeItem); + + while (treeNode) { + const node_data = treeNode.getData(); + if (node_data._type === 'server') { + serverInformation = node_data; + break; + } + + if (treeNode.hasParent()) { + treeNode = treeNode.parent(); + } else { + this.alertify.alert( + gettext(this.errorAlertTitle), + gettext('Please select server or child node from the browser tree.') + ); + break; + } + } + return serverInformation; + } + + hasBinariesConfiguration(serverInformation) { + const module = 'paths'; + let preference_name = 'pg_bin_dir'; + let msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.'); + + if ((serverInformation.type && serverInformation.type === 'ppas') || + serverInformation.server_type === 'ppas') { + preference_name = 'ppas_bin_dir'; + msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.'); + } + const preference = this.pgBrowser.get_preference(module, preference_name); + + if (preference) { + if (!preference.value) { + this.alertify.alert(gettext('Configuration required'), msg); + return false; + } + } else { + this.alertify.alert( + gettext(this.errorAlertTitle), + sprintf(gettext('Failed to load preference %s of module %s'), preference_name, module) + ); + return false; + } + return true; + } + + dialogName() { + return undefined; + } + + createOrGetDialog(dialogTitle, typeOfDialog) { + const dialogName = this.dialogName(typeOfDialog); + + if (!this.alertify[dialogName]) { + const self = this; + this.alertify.dialog(dialogName, function factory() { + return self.dialogFactory(dialogTitle, typeOfDialog); + }); + } + return this.alertify[dialogName]; + } + + dialogFactory(dialogTitle, typeOfDialog) { + const factory = new DialogFactory( + this.pgBrowser, + this.jquery, + this.alertify, + this.dialogModel, + this.backform, + this.dialogContainerSelector); + return factory.create(dialogTitle, typeOfDialog); + } + + canExecuteOnCurrentDatabase(aciTreeItem) { + const treeInfo = getTreeNodeHierarchyFromIdentifier.apply(this.pgBrowser, [aciTreeItem]); + + if (treeInfo.database && treeInfo.database._label.indexOf('=') >= 0) { + this.alertify.alert( + gettext(this.errorAlertTitle), + gettext('Databases with = symbols in the name cannot be backed up or restored using this utility.') + ); + return false; + } + + return true; + } +} diff --git a/web/pgadmin/static/js/alertify/dialog_factory.js b/web/pgadmin/static/js/alertify/dialog_factory.js new file mode 100644 index 00000000..500140b8 --- /dev/null +++ b/web/pgadmin/static/js/alertify/dialog_factory.js @@ -0,0 +1,52 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import * as BackupDialog from '../../../tools/backup/static/js/backup_dialog_wrapper'; +import {RestoreDialogWrapper} from '../../../tools/restore/static/js/restore_dialog_wrapper'; + +export class DialogFactory { + constructor(pgBrowser, $, + alertify, DialogModel, + backform, dialogContainerSelector) { + this.pgBrowser = pgBrowser; + this.jquery = $; + this.alertify = alertify; + this.dialogModel = DialogModel; + this.backform = backform; + this.dialogContainerSelector = dialogContainerSelector; + } + + create(dialogTitle, typeOfDialog) { + if (typeOfDialog === 'restore') { + return this.createRestoreDialog(dialogTitle, typeOfDialog); + } else { + return this.createBackupDialog(dialogTitle, typeOfDialog); + } + } + + createRestoreDialog(dialogTitle, typeOfDialog) { + return new RestoreDialogWrapper( + this.dialogContainerSelector, dialogTitle, typeOfDialog, + this.jquery, + this.pgBrowser, + this.alertify, + this.dialogModel, + this.backform); + } + + createBackupDialog(dialogTitle, typeOfDialog) { + return new BackupDialog.BackupDialogWrapper( + this.dialogContainerSelector, dialogTitle, typeOfDialog, + this.jquery, + this.pgBrowser, + this.alertify, + this.dialogModel, + this.backform); + } +} diff --git a/web/pgadmin/static/js/alertify/dialog_wrapper.js b/web/pgadmin/static/js/alertify/dialog_wrapper.js new file mode 100644 index 00000000..b5ff8204 --- /dev/null +++ b/web/pgadmin/static/js/alertify/dialog_wrapper.js @@ -0,0 +1,57 @@ +import * as commonUtils from '../utils'; + +export class DialogWrapper { + constructor( + dialogContainerSelector, dialogTitle, jquery, pgBrowser, + alertify, dialogModel, backform) { + this.hooks = { + onclose: function () { + if (this.view) { + this.view.remove({ + data: true, + internal: true, + silent: true, + }); + } + }, + }; + this.dialogContainerSelector = dialogContainerSelector; + this.dialogTitle = dialogTitle; + this.jquery = jquery; + this.pgBrowser = pgBrowser; + this.alertify = alertify; + this.dialogModel = dialogModel; + this.backform = backform; + } + + build() { + this.alertify.pgDialogBuild.apply(this); + } + + wasHelpButtonPressed(e) { + return e.button.element.name === 'dialog_help' + || e.button.element.name === 'object_help'; + } + + getSelectedNodeData(selectedTreeNode) { + if (!this.isNodeSelected(selectedTreeNode)) { + return undefined; + } + const treeNodeData = selectedTreeNode.getData(); + if (treeNodeData) { + return treeNodeData; + } + return undefined; + } + + focusOnDialog(dialog) { + dialog.$el.attr('tabindex', -1); + this.pgBrowser.keyboardNavigation.getDialogTabNavigator(dialog); + const container = dialog.$el.find('.tab-content:first > .tab-pane.active:first'); + commonUtils.findAndSetFocus(container); + } + + isNodeSelected(selectedTreeNode) { + return selectedTreeNode; + } +} diff --git a/web/pgadmin/static/js/menu/can_create.js b/web/pgadmin/static/js/menu/can_create.js new file mode 100644 index 00000000..476be437 --- /dev/null +++ b/web/pgadmin/static/js/menu/can_create.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 +// +///////////////////////////////////////////////////////////// + +export function canCreate(pgBrowser, childOfCatalogType, 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.anyFamilyMember(parentCatalogOfTableChild.bind(null, childOfCatalogType))) { + return false; + } + + return true; +} + +function parentCatalogOfTableChild(arg, node) { + if (arg === node.getData()._type) { + if (node.hasParent()) { + + let parent = node.parent(); + if ('catalog' === parent.getData()._type) { + return true; + } + } + } + + return false; +} diff --git a/web/pgadmin/static/js/menu/menu_enabled.js b/web/pgadmin/static/js/menu/menu_enabled.js new file mode 100644 index 00000000..05ff0f4a --- /dev/null +++ b/web/pgadmin/static/js/menu/menu_enabled.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 +// +////////////////////////////////////////////////////////////// + +function isNodeTypeSupported(backupSupportedNodes, nodeDataType, treeNode) { + return _.indexOf(backupSupportedNodes, nodeDataType) !== -1 + && ancestorWithTypeCatalogDoesNotExists(treeNode); +} + +export function isProvidedDataValid(treeNodeData) { + return !_.isUndefined(treeNodeData) && !_.isNull(treeNodeData); +} + +function doesNodeHaveMenu(treeNodeData) { + return (treeNodeData._type === 'database' && treeNodeData.allowConn) + || treeNodeData._type !== 'database'; +} + +function ancestorWithTypeCatalogDoesNotExists(treeNode) { + let currentNode = treeNode; + + while(currentNode.hasParent() && treeNode.parent().getData() !== null) { + if(currentNode.parent().getData()._type === 'catalog') { + return false; + } + + currentNode = currentNode.parent(); + } + + return true; +} + +export function menuEnabled(tree, backupSupportedNodes, treeNodeData, domTreeNode) { + let treeNode = tree.findNodeByDomElement(domTreeNode); + if (!treeNode) { + return false; + } + + if (isProvidedDataValid(treeNodeData)) { + return isNodeTypeSupported(backupSupportedNodes, treeNodeData._type, treeNode) + && doesNodeHaveMenu(treeNodeData); + } else { + return false; + } +} + + diff --git a/web/pgadmin/static/js/tree/pgadmin_tree_node.js b/web/pgadmin/static/js/tree/pgadmin_tree_node.js new file mode 100644 index 00000000..00f10d24 --- /dev/null +++ b/web/pgadmin/static/js/tree/pgadmin_tree_node.js @@ -0,0 +1,72 @@ +////////////////////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////////////////// + +/** + * This method received pgBrowser and new TreeNode object + * + * This method retrieves all the data that exists in the tree node and in + * `pgBrowser.Nodes` for all the parent node of the provided node. + * + * The 2 condition to get the information from pgBrowser.Nodes are: + * 1 - the variable _type of the tree node + * 2 - the presence of hasId in the pgBrowser.Nodes for the specific node + * + * Number 2 is used to ignore coll-* nodes as they do not add any useful + * information + */ +export function getTreeNodeHierarchyFromElement(pgBrowser, treeNode) { + return getTreeNodeHierarchy.call(pgBrowser, treeNode); +} + +/** + * This method received an ACI Tree JQuery node + * + * NOTE: this function need to be called on pgBrowser instance. + * getTreeNodeHierarchyFromIdentifier.apply(pgBrowser, [aciTreeNodeIdentifier]) + * + * This method retrieves all the data that exists in the tree node and in + * `pgBrowser.Nodes` for all the parent node of the provided node. + * + * The 2 condition to get the information from pgBrowser.Nodes are: + * 1 - the variable _type of the tree node + * 2 - the presence of hasId in the pgBrowser.Nodes for the specific node + * + * Number 2 is used to ignore coll-* nodes as they do not add any useful + * information + */ +export function getTreeNodeHierarchyFromIdentifier(aciTreeNodeIdentifier) { + let identifier = this.treeMenu.translateTreeNodeIdFromACITree(aciTreeNodeIdentifier); + let currentNode = this.treeMenu.findNode(identifier); + return getTreeNodeHierarchy.call(this, currentNode); +} + +export function getTreeNodeHierarchy(currentNode) { + let idx = 0; + let result = {}; + + do { + const currentNodeData = currentNode.getData(); + if (currentNodeData._type in this.Nodes && this.Nodes[currentNodeData._type].hasId) { + const nodeType = mapType(currentNodeData._type); + if (result[nodeType] === undefined) { + result[nodeType] = _.extend({}, currentNodeData, { + 'priority': idx, + }); + idx -= 1; + } + } + currentNode = currentNode.hasParent() ? currentNode.parent() : null; + } while (currentNode); + + return result; +} + +function mapType(type) { + return type === 'partition' ? 'table' : type; +} diff --git a/web/pgadmin/tools/backup/static/js/backup.js b/web/pgadmin/tools/backup/static/js/backup.js index 1b0b3628..d74ed6dd 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/menu/menu_enabled', ], function( gettext, url_for, $, _, S, alertify, Backbone, Backgrid, Backform, pgBrowser, -commonUtils +commonUtils, menuUtils, globalBackupDialog, menuEnabled ) { // 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,22 @@ commonUtils priority: 11, label: gettext('Backup...'), icon: 'fa fa-floppy-o', - enable: menu_enabled, + enable: menuEnabled.menuEnabled.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: menuEnabled.menuEnabled.bind(null, + pgBrowser.treeMenu, menuUtils.backupSupportedNodes), }); } @@ -521,542 +484,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 = $('
'); - // Find current/selected node - var t = pgBrowser.tree, - i = t.selected(), - d = i && i.length == 1 ? t.itemData(i) : undefined, - node = d && pgBrowser.Nodes[d._type]; - - if (!d) - return; - // Create treeInfo - var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]); - // Instance of backbone model - var newModel = new BackupModel({ - type: of_type, - }, { - node_info: treeInfo, - }), - fields = Backform.generateViewSchema( - treeInfo, newModel, 'create', node, treeInfo.server, true - ); - - var view = this.view = new Backform.Dialog({ - el: $container, - model: newModel, - schema: fields, - }); - // Add our class to alertify - $(this.elements.body.childNodes[0]).addClass( - 'alertify_tools_dialog_properties obj_properties' - ); - // Render dialog - view.render(); - - this.elements.content.appendChild($container.get(0)); - - var container = view.$el.find('.tab-content:first > .tab-pane.active:first'); - commonUtils.findAndSetFocus(container); - - // Listen to model & if filename is provided then enable Backup button - this.view.model.on('change', function() { - if (!_.isUndefined(this.get('file')) && this.get('file') !== '') { - this.errorModel.clear(); - self.__internal.buttons[2].element.disabled = false; - } else { - self.__internal.buttons[2].element.disabled = true; - this.errorModel.set('file', gettext('Please provide a filename')); - } - }); - }, - // Callback functions when click on the buttons of the Alertify dialogs - callback: function(e) { - // Fetch current server id - var t = pgBrowser.tree, - i = t.selected(), - d = i && i.length == 1 ? t.itemData(i) : undefined, - node = d && pgBrowser.Nodes[d._type]; - - if (e.button.element.name == 'dialog_help' || e.button.element.name == 'object_help') { - e.cancel = true; - pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'), - node, i, e.button.element.getAttribute('label')); - return; - } - - if (e.button['data-btn-name'] === 'backup') { - - if (!d) - return; - - var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]); - - var self = this, - baseUrl = url_for('backup.create_server_job', { - 'sid': treeInfo.server._id, - }), - args = this.view.model.toJSON(); - - $.ajax({ - url: baseUrl, - method: 'POST', - data: { - 'data': JSON.stringify(args), - }, - success: function(res) { - if (res.success) { - alertify.success(gettext('Backup job created.'), 5); - pgBrowser.Events.trigger('pgadmin-bgprocess:created', self); - } else { - console.warn(res); - } - }, - error: function(xhr) { - try { - var err = $.parseJSON(xhr.responseText); - alertify.alert( - gettext('Backup job failed.'), - err.errormsg - ); - } catch (e) { - console.warn(e.stack || e); - } - }, - }); - } - }, - }; - }); - } - alertify[DialogName](true).resizeTo('60%', '50%'); + start_backup_global_server: function(action, treeItem, params) { + let dialog = new globalBackupDialog.BackupDialog( + pgBrowser, + $, + alertify, + BackupModel + ); + dialog.draw(action, treeItem, params); }, // Callback to draw Backup Dialog for objects backup_objects: function(action, treeItem) { - - var i = treeItem || pgBrowser.tree.selected(), - server_data = null; - - while (i) { - var node_data = pgBrowser.tree.itemData(i); - if (node_data._type == 'server') { - server_data = node_data; - break; - } - - if (pgBrowser.tree.hasParent(i)) { - i = $(pgBrowser.tree.parent(i)); - } else { - alertify.alert( - gettext('Backup Error'), - gettext('Please select server or child node from tree.') - ); - break; - } - } - - if (!server_data) { - return; - } - - var module = 'paths', - preference_name = 'pg_bin_dir', - msg = gettext('Please set binary path for PostgreSQL Server from preferences.'); - - if ((server_data.type && server_data.type == 'ppas') || - server_data.server_type == 'ppas') { - preference_name = 'ppas_bin_dir'; - msg = gettext('Please set binary path for EDB Postgres Advanced Server from preferences.'); - } - - var preference = pgBrowser.get_preference(module, preference_name); - - if (preference) { - if (!preference.value) { - alertify.alert(gettext('Configuration required'), msg); - return; - } - } else { - alertify.alert( - gettext('Backup Error'), - S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value() - ); - return; - } - - var title = S(gettext('Backup (%s: %s)')), - tree = pgBrowser.tree, - item = treeItem || tree.selected(), - data = item && item.length == 1 && tree.itemData(item), - node = data && data._type && pgBrowser.Nodes[data._type]; - - if (!node) - return; - - var treeInfo = node.getTreeNodeHierarchy.apply(node, [item]); - - if (treeInfo.database._label.indexOf('=') >= 0) { - alertify.alert( - gettext('Backup error'), - gettext('Backup job creation failed. '+ - 'Databases with = symbols in the name cannot be backed up using this utility.') - ); - return; - } - - title = title.sprintf(node.label, data.label).value(); - - if (!alertify.backup_objects) { - // Create Dialog title on the fly with node details - alertify.dialog('backup_objects', function factory() { - return { - main: function(title) { - this.set('title', title); - }, - build: function() { - alertify.pgDialogBuild.apply(this); - }, - setup: function() { - return { - buttons: [{ - text: '', - className: 'btn btn-default pull-left fa fa-lg fa-info', - attrs: { - name: 'object_help', - type: 'button', - url: 'backup.html', - label: gettext('Backup'), - }, - }, { - text: '', - key: 112, - className: 'btn btn-default pull-left fa fa-lg fa-question', - attrs: { - name: 'dialog_help', - type: 'button', - label: gettext('Backup'), - url: url_for('help.static', { - 'filename': 'backup_dialog.html', - }), - }, - }, { - text: gettext('Backup'), - key: 13, - className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button', - 'data-btn-name': 'backup', - }, { - text: gettext('Cancel'), - key: 27, - className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button', - 'data-btn-name': 'cancel', - }], - // Set options for dialog - options: { - title: title, - //disable both padding and overflow control. - padding: !1, - overflow: !1, - model: 0, - resizable: true, - maximizable: true, - pinnable: false, - closableByDimmer: false, - modal: false, - }, - }; - }, - hooks: { - // triggered when the dialog is closed - onclose: function() { - if (this.view) { - this.view.remove({ - data: true, - internal: true, - silent: true, - }); - } - }, - }, - prepare: function() { - var self = this; - // Disable Backup button until user provides Filename - this.__internal.buttons[2].element.disabled = true; - var $container = $(''); - var t = pgBrowser.tree, - i = t.selected(), - d = i && i.length == 1 ? t.itemData(i) : undefined, - node = d && pgBrowser.Nodes[d._type]; - - if (!d) - return; - - var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]); - - var newModel = new BackupObjectModel({}, { - node_info: treeInfo, - }), - fields = Backform.generateViewSchema( - treeInfo, newModel, 'create', node, treeInfo.server, true - ); - - var view = this.view = new Backform.Dialog({ - el: $container, - model: newModel, - schema: fields, - }); - - $(this.elements.body.childNodes[0]).addClass( - 'alertify_tools_dialog_properties obj_properties' - ); - - view.render(); - - this.elements.content.appendChild($container.get(0)); - - if(view) { - view.$el.attr('tabindex', -1); - // var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view); - pgBrowser.keyboardNavigation.getDialogTabNavigator(view); - var container = view.$el.find('.tab-content:first > .tab-pane.active:first'); - commonUtils.findAndSetFocus(container); - } - // Listen to model & if filename is provided then enable Backup button - this.view.model.on('change', function() { - if (!_.isUndefined(this.get('file')) && this.get('file') !== '') { - this.errorModel.clear(); - self.__internal.buttons[2].element.disabled = false; - } else { - self.__internal.buttons[2].element.disabled = true; - this.errorModel.set('file', gettext('Please provide filename')); - } - }); - - }, - // Callback functions when click on the buttons of the Alertify dialogs - callback: function(e) { - // Fetch current server id - var t = pgBrowser.tree, - i = t.selected(), - d = i && i.length == 1 ? t.itemData(i) : undefined, - node = d && pgBrowser.Nodes[d._type]; - - if (e.button.element.name == 'dialog_help' || e.button.element.name == 'object_help') { - e.cancel = true; - pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'), - node, i, e.button.element.getAttribute('label')); - return; - } - - if (e.button['data-btn-name'] === 'backup') { - if (!d) - return; - - var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]); - - // Set current database into model - this.view.model.set('database', treeInfo.database._label); - - // We will remove once object tree is implemented - // If selected node is Schema then add it in model - if (d._type == 'schema') { - var schemas = []; - schemas.push(d._label); - this.view.model.set('schemas', schemas); - } - // If selected node is Table then add it in model along with - // its schema - if (d._type == 'table') { - this.view.model.set( - 'tables', [ - [treeInfo.schema._label, d._label], - ] - ); - } - - // Remove ratio attribute from model if it has empty string. - // The valid value can be between 0 to 9. - if (_.isEmpty(this.view.model.get('ratio'))) { - this.view.model.unset('ratio'); - } - - var self = this, - baseUrl = url_for('backup.create_object_job', { - 'sid': treeInfo.server._id, - }), - args = this.view.model.toJSON(); - - $.ajax({ - url: baseUrl, - method: 'POST', - data: { - 'data': JSON.stringify(args), - }, - success: function(res) { - if (res.success) { - alertify.success(gettext('Backup job created.'), 5); - pgBrowser.Events.trigger('pgadmin-bgprocess:created', self); - } - }, - error: function(xhr) { - try { - var err = $.parseJSON(xhr.responseText); - alertify.alert( - gettext('Backup job failed.'), - err.errormsg - ); - } catch (e) { - console.warn(e.stack || e); - } - }, - }); - } - }, - }; - }); - } - alertify.backup_objects(title).resizeTo('65%', '60%'); + let dialog = new globalBackupDialog.BackupDialog( + pgBrowser, + $, + alertify, + BackupObjectModel + ); + dialog.draw(action, treeItem, null); }, }; return pgBrowser.Backup; diff --git a/web/pgadmin/tools/backup/static/js/backup_dialog.js b/web/pgadmin/tools/backup/static/js/backup_dialog.js new file mode 100644 index 00000000..c74c376c --- /dev/null +++ b/web/pgadmin/tools/backup/static/js/backup_dialog.js @@ -0,0 +1,69 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import gettext from '../../../../static/js/gettext'; +import Backform from '../../../../static/js/backform.pgadmin'; +import {Dialog} from '../../../../static/js/alertify/dialog'; + +export class BackupDialog extends Dialog { + constructor(pgBrowser, $, alertify, BackupModel, backform = Backform) { + super('Backup Error', + '', + 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..fdf400af --- /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 {isProvidedDataValid} from '../../../../static/js/menu/menu_enabled'; + +export const backupSupportedNodes = [ + 'database', 'schema', 'table', 'partition', +]; + +function isNodeAServerAndConnected(treeNodeData) { + return (('server' === treeNodeData._type) && treeNodeData.connected); +} + +export function menuEnabledServer(treeNodeData) { + return isProvidedDataValid(treeNodeData) + && isNodeAServerAndConnected(treeNodeData); +} diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid.js b/web/pgadmin/tools/datagrid/static/js/datagrid.js index b0ed60f6..520a9ce5 100644 --- a/web/pgadmin/tools/datagrid/static/js/datagrid.js +++ b/web/pgadmin/tools/datagrid/static/js/datagrid.js @@ -1,10 +1,14 @@ define('pgadmin.datagrid', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'pgadmin.alertifyjs', 'sources/pgadmin', 'bundled_codemirror', - 'sources/sqleditor_utils', 'backbone', 'wcdocker', + 'sources/sqleditor_utils', 'backbone', + 'tools/datagrid/static/js/show_data', + 'tools/datagrid/static/js/get_panel_title', + 'tools/datagrid/static/js/show_query_tool', + 'wcdocker', ], function( gettext, url_for, $, _, alertify, pgAdmin, codemirror, sqlEditorUtils, - Backbone + Backbone, showData, panelTitle, showQueryTool ) { // Some scripts do export their object in the window only. // Generally the one, which do no have AMD support. @@ -161,55 +165,7 @@ define('pgadmin.datagrid', [ // This is a callback function to show data when user click on menu item. show_data_grid: function(data, i) { - var self = this, - d = pgAdmin.Browser.tree.itemData(i); - if (d === undefined) { - alertify.alert( - gettext('Data Grid Error'), - gettext('No object selected.') - ); - return; - } - - // Get the parent data from the tree node hierarchy. - var node = pgBrowser.Nodes[d._type], - parentData = node.getTreeNodeHierarchy(i); - - // If server, database or schema is undefined then return from the function. - if (parentData.server === undefined || parentData.database === undefined) { - return; - } - // If schema, view, catalog object all are undefined then return from the function. - if (parentData.schema === undefined && parentData.view === undefined && - parentData.catalog === undefined) { - return; - } - - var nsp_name = ''; - - if (parentData.schema != undefined) { - nsp_name = parentData.schema.label; - } - else if (parentData.view != undefined) { - nsp_name = parentData.view.label; - } - else if (parentData.catalog != undefined) { - nsp_name = parentData.catalog.label; - } - var url_params = { - 'cmd_type': data.mnuid, - 'obj_type': d._type, - 'sgid': parentData.server_group._id, - 'sid': parentData.server._id, - 'did': parentData.database._id, - 'obj_id': d._id, - }; - - var baseUrl = url_for('datagrid.initialize_datagrid', url_params); - var grid_title = parentData.server.label + ' - ' + parentData.database.label + ' - ' - + nsp_name + '.' + d.label; - - self.create_transaction(baseUrl, null, 'false', parentData.server.server_type, '', grid_title, ''); + showData.showDataGrid(this, pgBrowser, alertify, data, i); }, // This is a callback function to show filtered data when user click on menu item. @@ -384,63 +340,12 @@ define('pgadmin.datagrid', [ }, get_panel_title: function() { - // Get the parent data from the tree node hierarchy. - var tree = pgAdmin.Browser.tree, - selected_item = tree.selected(), - item_data = tree.itemData(selected_item); - - var node = pgBrowser.Nodes[item_data._type], - parentData = node.getTreeNodeHierarchy(selected_item); - - // If server, database is undefined then return from the function. - if (parentData.server === undefined) { - return; - } - // If Database is not available then use default db - var db_label = parentData.database ? parentData.database.label - : parentData.server.db; - - var grid_title = db_label + ' on ' + parentData.server.user.name + '@' + - parentData.server.label; - return grid_title; + return panelTitle.getPanelTitle(pgBrowser); }, // This is a callback function to show query tool when user click on menu item. - show_query_tool: function(url, i, panel_title) { - var sURL = url || '', - d = pgAdmin.Browser.tree.itemData(i); - - panel_title = panel_title || ''; - if (d === undefined) { - alertify.alert( - gettext('Query Tool Error'), - gettext('No object selected.') - ); - return; - } - - // Get the parent data from the tree node hierarchy. - var node = pgBrowser.Nodes[d._type], - parentData = node.getTreeNodeHierarchy(i); - - // If server, database is undefined then return from the function. - if (parentData.server === undefined) { - return; - } - - var url_params = { - 'sgid': parentData.server_group._id, - 'sid': parentData.server._id, - }, - url_endpoint = 'datagrid.initialize_query_tool'; - // If database not present then use Maintenance database - // We will handle this at server side - if (parentData.database) { - url_params['did'] = parentData.database._id; - url_endpoint = 'datagrid.initialize_query_tool_with_did'; - } - var baseUrl = url_for(url_endpoint, url_params); - - this.create_transaction(baseUrl, null, 'true', parentData.server.server_type, sURL, panel_title, '', false); + show_query_tool: function(url, aciTreeIdentifier, panelTitle) { + showQueryTool.showQueryTool(this, pgBrowser, alertify, url, + aciTreeIdentifier, panelTitle); }, create_transaction: function(baseUrl, target, is_query_tool, server_type, sURL, panel_title, sql_filter, recreate) { var self = this; diff --git a/web/pgadmin/tools/datagrid/static/js/get_panel_title.js b/web/pgadmin/tools/datagrid/static/js/get_panel_title.js new file mode 100644 index 00000000..64b3a3d2 --- /dev/null +++ b/web/pgadmin/tools/datagrid/static/js/get_panel_title.js @@ -0,0 +1,33 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import {getTreeNodeHierarchyFromIdentifier} from '../../../../static/js/tree/pgadmin_tree_node'; + +function getDatabaseLabel(parentData) { + return parentData.database ? parentData.database.label + : parentData.server.db; +} + +function isServerInformationAvailable(parentData) { + return parentData.server === undefined; +} + +export function getPanelTitle(pgBrowser) { + const selected_item = pgBrowser.treeMenu.selected(); + + const parentData = getTreeNodeHierarchyFromIdentifier + .call(pgBrowser, selected_item); + if (isServerInformationAvailable(parentData)) { + return; + } + + const db_label = getDatabaseLabel(parentData); + + return `${db_label} on ${parentData.server.user.name}@${parentData.server.label}`; +} diff --git a/web/pgadmin/tools/datagrid/static/js/show_data.js b/web/pgadmin/tools/datagrid/static/js/show_data.js new file mode 100644 index 00000000..373c97cd --- /dev/null +++ b/web/pgadmin/tools/datagrid/static/js/show_data.js @@ -0,0 +1,92 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// +import gettext from '../../../../static/js/gettext'; +import url_for from '../../../../static/js/url_for'; +import {getTreeNodeHierarchyFromIdentifier} from '../../../../static/js/tree/pgadmin_tree_node'; + +export function showDataGrid( + datagrid, + pgBrowser, + alertify, + connectionData, + aciTreeIdentifier +) { + const node = pgBrowser.treeMenu.findNodeByDomElement(aciTreeIdentifier); + if (node === undefined || !node.getData()) { + alertify.alert( + gettext('Data Grid Error'), + gettext('No object selected.') + ); + return; + } + + const parentData = getTreeNodeHierarchyFromIdentifier.call( + pgBrowser, + aciTreeIdentifier + ); + + if (hasServerOrDatabaseConfiguration(parentData) + || !hasSchemaOrCatalogOrViewInformation(parentData)) { + return; + } + + let namespaceName = retrieveNameSpaceName(parentData); + const baseUrl = generateUrl(connectionData, node.getData(), parentData); + const grid_title = generateDatagridTitle(parentData, namespaceName, node.getData()); + + datagrid.create_transaction( + baseUrl, + null, + 'false', + parentData.server.server_type, + '', + grid_title, + '' + ); +} + + +function retrieveNameSpaceName(parentData) { + if (parentData.schema !== undefined) { + return parentData.schema.label; + } + else if (parentData.view !== undefined) { + return parentData.view.label; + } + else if (parentData.catalog !== undefined) { + return parentData.catalog.label; + } + return ''; +} + +function generateUrl(connectionData, nodeData, parentData) { + const url_params = { + 'cmd_type': connectionData.mnuid, + 'obj_type': nodeData._type, + 'sgid': parentData.server_group._id, + 'sid': parentData.server._id, + 'did': parentData.database._id, + 'obj_id': nodeData._id, + }; + + return url_for('datagrid.initialize_datagrid', url_params); +} + +function hasServerOrDatabaseConfiguration(parentData) { + return parentData.server === undefined || parentData.database === undefined; +} + +function hasSchemaOrCatalogOrViewInformation(parentData) { + return parentData.schema !== undefined || parentData.view !== undefined || + parentData.catalog !== undefined; +} + +function generateDatagridTitle(parentData, namespaceName, nodeData) { + return `${parentData.server.label} - ${parentData.database.label} - ${namespaceName}.${nodeData.label}`; +} diff --git a/web/pgadmin/tools/datagrid/static/js/show_query_tool.js b/web/pgadmin/tools/datagrid/static/js/show_query_tool.js new file mode 100644 index 00000000..0eb12b8b --- /dev/null +++ b/web/pgadmin/tools/datagrid/static/js/show_query_tool.js @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import gettext from '../../../../static/js/gettext'; +import url_for from '../../../../static/js/url_for'; +import {getTreeNodeHierarchyFromIdentifier} from '../../../../static/js/tree/pgadmin_tree_node'; + +function hasDatabaseInformation(parentData) { + return parentData.database; +} + +function generateUrl(parentData) { + let url_endpoint = 'datagrid.initialize_query_tool'; + let url_params = { + 'sgid': parentData.server_group._id, + 'sid': parentData.server._id, + }; + + if (hasDatabaseInformation(parentData)) { + url_params['did'] = parentData.database._id; + url_endpoint = 'datagrid.initialize_query_tool_with_did'; + } + + return url_for(url_endpoint, url_params); +} + +function hasServerInformations(parentData) { + return parentData.server === undefined; +} + +export function showQueryTool(datagrid, pgBrowser, alertify, url, + aciTreeIdentifier, panelTitle) { + const sURL = url || ''; + const queryToolTitle = panelTitle || ''; + + const currentNode = pgBrowser.treeMenu.findNodeByDomElement(aciTreeIdentifier); + if (currentNode === undefined) { + alertify.alert( + gettext('Query Tool Error'), + gettext('No object selected.') + ); + return; + } + + const parentData = getTreeNodeHierarchyFromIdentifier.call( + pgBrowser, aciTreeIdentifier); + + if (hasServerInformations(parentData)) { + return; + } + + const baseUrl = generateUrl(parentData); + + datagrid.create_transaction( + baseUrl, null, 'true', + parentData.server.server_type, sURL, queryToolTitle, '', false); +} diff --git a/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js b/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js index 750887ec..cf234f1d 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,16 @@ 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/menu/menu_enabled', + + 'backgrid.select.all', 'backgrid.filter', 'pgadmin.browser.server.privilege', 'pgadmin.browser.wizard', ], function( gettext, url_for, $, _, Backbone, Alertify, Backgrid, Backform, pgBrowser, - pgNode + pgNode, menuUtils, menuEnabled ) { // if module is already initialized, refer to that. @@ -143,41 +147,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 +156,23 @@ define([ priority: 14, label: gettext('Grant Wizard...'), icon: 'fa fa-unlock-alt', - enable: menu_enabled, + enable: menuEnabled.menuEnabled.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: menuEnabled.menuEnabled.bind(null, + pgBrowser.treeMenu, menuUtils.supportedNodes), }); } pgBrowser.add_menus(menus); diff --git a/web/pgadmin/tools/grant_wizard/static/js/menu_utils.js b/web/pgadmin/tools/grant_wizard/static/js/menu_utils.js new file mode 100644 index 00000000..b56ce3cd --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/static/js/menu_utils.js @@ -0,0 +1,16 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +export const supportedNodes = [ + 'schema', 'coll-function', 'coll-sequence', + 'coll-table', 'coll-view', 'coll-procedure', + 'coll-mview', 'database', 'coll-trigger_function', +]; + + diff --git a/web/pgadmin/tools/import_export/static/js/import_export.js b/web/pgadmin/tools/import_export/static/js/import_export.js index 3058f122..bbb045cd 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,13 @@ 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/menu/menu_enabled', + + 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui', ], function( gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, -Backform, commonUtils +Backform, commonUtils, menuEnabled ) { pgAdmin = pgAdmin || window.pgAdmin || {}; @@ -383,25 +386,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 +397,7 @@ Backform, commonUtils priority: 10, label: gettext('Import/Export...'), icon: 'fa fa-shopping-cart', - enable: menu_enabled, + enable: menuEnabled.menuEnabled.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..9092eb73 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/menu/menu_enabled', 'pgadmin.backform', 'pgadmin.backgrid', 'pgadmin.browser.node.ui', ], function( gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, - Backform, commonUtils + Backform, commonUtils, + menuUtils, menuEnabled ) { 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,23 @@ define([ priority: 10, label: gettext('Maintenance...'), icon: 'fa fa-wrench', - enable: menu_enabled, + enable: menuEnabled.menuEnabled.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: menuEnabled.menuEnabled.bind(null, + pgBrowser.treeMenu, menuUtils.maintenanceSupportedNodes), }); } pgBrowser.add_menus(menus); diff --git a/web/pgadmin/tools/maintenance/static/js/menu_utils.js b/web/pgadmin/tools/maintenance/static/js/menu_utils.js new file mode 100644 index 00000000..8cde1baa --- /dev/null +++ b/web/pgadmin/tools/maintenance/static/js/menu_utils.js @@ -0,0 +1,13 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +export const maintenanceSupportedNodes = [ + 'database', 'table', 'primary_key', + 'unique_constraint', 'index', 'partition', +]; diff --git a/web/pgadmin/tools/restore/static/js/menu_utils.js b/web/pgadmin/tools/restore/static/js/menu_utils.js new file mode 100644 index 00000000..2d35c951 --- /dev/null +++ b/web/pgadmin/tools/restore/static/js/menu_utils.js @@ -0,0 +1,18 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +export const restoreSupportedNodes = [ + 'database', + 'schema', + 'table', + 'function', + 'trigger', + 'index', + 'partition', +]; diff --git a/web/pgadmin/tools/restore/static/js/restore.js b/web/pgadmin/tools/restore/static/js/restore.js index 585b9729..eaa7a15e 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/menu/menu_enabled', + 'tools/restore/static/js/restore_dialog', ], function( gettext, url_for, $, _, Backbone, S, alertify, pgBrowser, Backgrid, Backform, -commonUtils +commonUtils, menuUtils, menuEnabled, 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,22 @@ commonUtils priority: 13, label: gettext('Restore...'), icon: 'fa fa-upload', - enable: menu_enabled, + enable: menuEnabled.menuEnabled.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: menuEnabled.menuEnabled.bind(null, + pgBrowser.treeMenu, menuUtils.restoreSupportedNodes), }); } @@ -391,318 +342,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 = $(''); - 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', + '', + 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 === '') { + return backupJQueryContainerSpy; + } else if (selector === backupNode.elements.body.childNodes[0]) { + return backupNodeChildNodeSpy; + } + }); + + }); + + describe('#prepare', () => { + beforeEach(() => { + alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']); + backupDialogWrapper = new BackupDialogWrapper( + '', + '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( + '', + '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( + '', + '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( + '', + '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( + '', + 'backupDialogTitle', + 'backup_objects', + jquerySpy, + pgBrowser, + alertifySpy, + dialogModelKlassSpy, + backform + ); + + treeInfo = { + database: { + _label: 'some-database-label', + }, + schema: { + _label: 'some-treeinfo-label', + }, + }; + + model = new FakeModel(); + selectedTreeNode = new TreeNode('some-selected-node', + {_type: 'some-type', _label: 'some-selected-label'}, + []); + backupDialogWrapper.view = { + model: model, + }; + }); + + it('sets the database label on the model', () => { + backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); + expect(model.toJSON()).toEqual({ + 'database': 'some-database-label', + }); + }); + + context('when the selected is a schema type', () => { + beforeEach(() => { + selectedTreeNode = new TreeNode('some-selected-node', + {_type: 'schema', _label: 'some-schema-label'}, + []); + }); + + it('sets the schema label on the model', () => { + backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); + expect(model.toJSON()).toEqual({ + 'database': 'some-database-label', + 'schemas': ['some-schema-label'], + }); + }); + }); + + context('when the selected is a table type', () => { + beforeEach(() => { + selectedTreeNode = new TreeNode('some-selected-node', + {_type: 'table', _label: 'some-table-label'}, + []); + }); + + it('sets the schema label on the model', () => { + backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); + expect(model.toJSON()).toEqual({ + 'database': 'some-database-label', + 'tables': [['some-treeinfo-label', 'some-table-label']], + }); + }); + }); + + context('when the model has no ratio value', () => { + beforeEach(() => { + model.set('ratio', ''); + }); + + it('sets clears the ratio value', () => { + backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); + expect(model.get('ratio')).toBeUndefined(); + }); + }); + + context('when the model has a valid ratio value', () => { + beforeEach(() => { + model.set('ratio', '0.25'); + }); + + it('sets clears the ratio value', () => { + backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); + expect(model.get('ratio')).toEqual('0.25'); + }); + }); + }); + }); +}); diff --git a/web/regression/javascript/backup/global_server_backup_dialog_spec.js b/web/regression/javascript/backup/global_server_backup_dialog_spec.js new file mode 100644 index 00000000..86df672e --- /dev/null +++ b/web/regression/javascript/backup/global_server_backup_dialog_spec.js @@ -0,0 +1,168 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// +import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog'; +import {TreeFake} from '../tree/tree_fake'; + +const context = describe; + +describe('GlobalServerBackupDialog', () => { + let backupDialog; + let pgBrowser; + let jquerySpy; + let alertifySpy; + let backupModelSpy; + + + let rootNode; + let serverTreeNode; + let ppasServerTreeNode; + + beforeEach(() => { + pgBrowser = { + treeMenu: new TreeFake(), + Nodes: { + server: jasmine.createSpyObj('Node[server]', ['getTreeNodeHierarchy']), + }, + }; + pgBrowser.Nodes.server.hasId = true; + jquerySpy = jasmine.createSpy('jquerySpy'); + backupModelSpy = jasmine.createSpy('backupModelSpy'); + + rootNode = pgBrowser.treeMenu.addNewNode('level1', {}, undefined, []); + serverTreeNode = pgBrowser.treeMenu.addNewNode('level1.1', { + _type: 'server', + _id: 10, + }, undefined, ['level1']); + ppasServerTreeNode = pgBrowser.treeMenu.addNewNode('level1.2', { + _type: 'server', + server_type: 'ppas', + }, undefined, ['level1']); + pgBrowser.treeMenu.addNewNode('level3', {}, undefined, ['level1', 'level1.2']); + pgBrowser.treeMenu.addNewNode('level3.1', undefined, undefined, ['level1', 'level1.2', 'level3']); + }); + + describe('#draw', () => { + beforeEach(() => { + alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']); + alertifySpy['BackupDialog_globals'] = jasmine.createSpy('BackupDialog_globals'); + alertifySpy['BackupDialog_server'] = jasmine.createSpy('BackupDialog_server'); + backupDialog = new BackupDialog( + pgBrowser, + jquerySpy, + alertifySpy, + backupModelSpy + ); + + pgBrowser.get_preference = jasmine.createSpy('get_preferences'); + }); + + context('there are no ancestors of the type server', () => { + it('does not create a dialog', () => { + pgBrowser.treeMenu.selectNode([{id: 'level1'}]); + backupDialog.draw(null, null, null); + expect(alertifySpy['BackupDialog_globals']).not.toHaveBeenCalled(); + expect(alertifySpy['BackupDialog_server']).not.toHaveBeenCalled(); + }); + + it('display an alert with a Backup Error', () => { + backupDialog.draw(null, [rootNode], null); + expect(alertifySpy.alert).toHaveBeenCalledWith( + 'Backup Error', + 'Please select server or child node from the browser tree.' + ); + }); + }); + + context('there is an ancestor of the type server', () => { + context('no preference can be found', () => { + beforeEach(() => { + pgBrowser.get_preference.and.returnValue(undefined); + }); + + context('server is a ppas server', () => { + it('display an alert with "Backup Error"', () => { + backupDialog.draw(null, [serverTreeNode], null); + expect(alertifySpy.alert).toHaveBeenCalledWith( + 'Backup Error', + 'Failed to load preference pg_bin_dir of module paths' + ); + }); + }); + + context('server is not a ppas server', () => { + it('display an alert with "Backup Error"', () => { + backupDialog.draw(null, [ppasServerTreeNode], null); + expect(alertifySpy.alert).toHaveBeenCalledWith( + 'Backup Error', + 'Failed to load preference ppas_bin_dir of module paths' + ); + }); + }); + }); + + context('preference can be found', () => { + context('binary folder is not configured', () => { + beforeEach(() => { + pgBrowser.get_preference.and.returnValue({}); + }); + + context('server is a ppas server', () => { + it('display an alert with "Configuration required"', () => { + backupDialog.draw(null, [serverTreeNode], null); + expect(alertifySpy.alert).toHaveBeenCalledWith( + 'Configuration required', + 'Please configure the PostgreSQL Binary Path in the Preferences dialog.' + ); + }); + }); + + context('server is not a ppas server', () => { + it('display an alert with "Configuration required"', () => { + backupDialog.draw(null, [ppasServerTreeNode], null); + expect(alertifySpy.alert).toHaveBeenCalledWith( + 'Configuration required', + 'Please configure the EDB Advanced Server Binary Path in the Preferences dialog.' + ); + }); + }); + }); + + context('binary folder is configured', () => { + let globalResizeToSpy; + let serverResizeToSpy; + beforeEach(() => { + globalResizeToSpy = jasmine.createSpyObj('globals', ['resizeTo']); + alertifySpy['BackupDialog_globals'].and + .returnValue(globalResizeToSpy); + serverResizeToSpy = jasmine.createSpyObj('server', ['resizeTo']); + alertifySpy['BackupDialog_server'].and + .returnValue(serverResizeToSpy); + pgBrowser.get_preference.and.returnValue({value: '/some/path'}); + }); + + context('dialog for global backup', () => { + it('displays the dialog', () => { + backupDialog.draw(null, [serverTreeNode], {globals: true}); + expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true); + expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%'); + }); + }); + + context('dialog for server backup', () => { + it('displays the dialog', () => { + backupDialog.draw(null, [serverTreeNode], {server: true}); + expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true); + expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%'); + }); + }); + }); + }); + }); + }); +}); diff --git a/web/regression/javascript/backup/menu_utils_spec.js b/web/regression/javascript/backup/menu_utils_spec.js new file mode 100644 index 00000000..9435d699 --- /dev/null +++ b/web/regression/javascript/backup/menu_utils_spec.js @@ -0,0 +1,55 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + + +import {menuEnabledServer} from '../../../pgadmin/tools/backup/static/js/menu_utils'; + +const context = describe; + +describe('backup.menuUtils', () => { + describe('#menuEnabledServer', () => { + context('provided node data is undefined', () => { + it('returns false', () => { + expect(menuEnabledServer(undefined)).toBe(false); + }); + }); + + context('provided node data is null', () => { + it('returns false', () => { + expect(menuEnabledServer(null)).toBe(false); + }); + }); + + context('current node type is not of the type server', () => { + it('returns false', () => { + expect(menuEnabledServer({_type: 'schema'})).toBe(false); + }); + }); + + context('current node type is of the type server', () => { + context('is connected', () => { + it('returns true', () => { + expect(menuEnabledServer({ + _type: 'server', + connected: true, + })).toBe(true); + }); + }); + context('is not connected', () => { + it('returns false', () => { + expect(menuEnabledServer({ + _type: 'server', + connected: false, + })).toBe(false); + }); + }); + }); + }); +}); + diff --git a/web/regression/javascript/common_keyboard_shortcuts_spec.js b/web/regression/javascript/common_keyboard_shortcuts_spec.js index 9ea31efd..e27929bf 100644 --- a/web/regression/javascript/common_keyboard_shortcuts_spec.js +++ b/web/regression/javascript/common_keyboard_shortcuts_spec.js @@ -11,10 +11,6 @@ import keyboardShortcuts from 'sources/keyboard_shortcuts'; describe('the keyboard shortcuts', () => { const F1_KEY = 112; - // const EDIT_KEY = 71; // Key: G -> Grid values - // const LEFT_ARROW_KEY = 37; - // const RIGHT_ARROW_KEY = 39; - // const MOVE_NEXT = 'right'; let debuggerElementSpy, event, debuggerUserShortcutSpy; debuggerUserShortcutSpy = jasmine.createSpyObj( diff --git a/web/regression/javascript/datagrid/get_panel_title_spec.js b/web/regression/javascript/datagrid/get_panel_title_spec.js new file mode 100644 index 00000000..8a344a84 --- /dev/null +++ b/web/regression/javascript/datagrid/get_panel_title_spec.js @@ -0,0 +1,82 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import {getPanelTitle} from '../../../pgadmin/tools/datagrid/static/js/get_panel_title'; +import {TreeFake} from '../tree/tree_fake'; +import {TreeNode} from '../../../pgadmin/static/js/tree/tree'; + +const context = describe; + +describe('#getPanelTitle', () => { + let pgBrowser; + let tree; + beforeEach(() => { + tree = new TreeFake(); + pgBrowser = { + treeMenu: tree, + Nodes: { + server: { + hasId: true, + _type: 'server', + }, + database: { + hasId: true, + _type: 'database', + }, + }, + }; + }); + + context('selected node does not belong to a server', () => { + it('returns undefined', () => { + const root = tree.addNewNode('level1', {_type: 'server_groups'}); + tree.addChild(root, new TreeNode('level1.1', {_type: 'other'})); + tree.selectNode([{id: 'level1'}]); + expect(getPanelTitle(pgBrowser)).toBeUndefined(); + }); + }); + + context('selected node belong to a server', () => { + context('selected node does not belong to a database', () => { + it('returns the server label and the username', () => { + tree.addNewNode('level1', { + _type: 'server', + db: 'other db label', + user: {name: 'some user name'}, + label: 'server label', + }, []); + + tree.selectNode([{id: 'level1'}]); + expect(getPanelTitle(pgBrowser)) + .toBe('other db label on some user name@server label'); + }); + }); + + context('selected node belongs to a database', () => { + it('returns the database label and the username', () => { + const root = tree.addNewNode('level1', { + _type: 'server', + db: 'other db label', + user: {name: 'some user name'}, + label: 'server label', + }); + const level1 = new TreeNode('level1.1', { + _type: 'database', + label: 'db label', + }); + tree.addChild(root, level1); + tree.addChild(level1, + new TreeNode('level1.1.1', {_type: 'table'})); + tree.selectNode([{id: 'level1.1.1'}]); + expect(getPanelTitle(pgBrowser)) + .toBe('db label on some user name@server label'); + }); + }); + }); +}); diff --git a/web/regression/javascript/datagrid/show_data_spec.js b/web/regression/javascript/datagrid/show_data_spec.js new file mode 100644 index 00000000..80d25eb3 --- /dev/null +++ b/web/regression/javascript/datagrid/show_data_spec.js @@ -0,0 +1,171 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import {showDataGrid} from '../../../pgadmin/tools/datagrid/static/js/show_data'; +import {TreeFake} from '../tree/tree_fake'; +import {TreeNode} from '../../../pgadmin/static/js/tree/tree'; + +const context = describe; + +describe('#show_data', () => { + let datagrid; + let pgBrowser; + let alertify; + beforeEach(() => { + alertify = jasmine.createSpyObj('alertify', ['alert']); + datagrid = { + create_transaction: jasmine.createSpy('create_transaction'), + }; + pgBrowser = { + treeMenu: new TreeFake(), + Nodes: { + server_group: { + _type: 'server_group', + hasId: true, + }, + server: { + _type: 'server', + hasId: true, + }, + database: { + _type: 'database', + hasId: true, + }, + schema: { + _type: 'schema', + hasId: true, + }, + view: { + _type: 'view', + hasId: true, + }, + catalog: { + _type: 'catalog', + hasId: true, + }, + }, + }; + const parent = pgBrowser.treeMenu.addNewNode('parent', {_type: 'parent'}, []); + const serverGroup1 = new TreeNode('server_group1', { + _type: 'server_group', + _id: 1, + }); + pgBrowser.treeMenu.addChild(parent, serverGroup1); + + const server1 = new TreeNode('server1', { + _type: 'server', + label: 'server1', + server_type: 'pg', + _id: 2, + }, ['parent', 'server_group1']); + pgBrowser.treeMenu.addChild(serverGroup1, server1); + + const database1 = new TreeNode('database1', { + _type: 'database', + label: 'database1', + _id: 3, + }, ['parent', 'server_group1', 'server1']); + pgBrowser.treeMenu.addChild(server1, database1); + + const schema1 = new TreeNode('schema1', { + _type: 'schema', + label: 'schema1', + _id: 4, + }); + pgBrowser.treeMenu.addChild(database1, schema1); + + const view1 = new TreeNode('view1', { + _type: 'view', + label: 'view1', + _id: 5, + }, ['parent', 'server_group1', 'server1', 'database1']); + pgBrowser.treeMenu.addChild(database1, view1); + + const catalog1 = new TreeNode('catalog1', { + _type: 'catalog', + label: 'catalog1', + _id: 6, + }, ['parent', 'server_group1', 'server1', 'database1']); + pgBrowser.treeMenu.addChild(database1, catalog1); + }); + + context('cannot find the tree node', () => { + it('does not create a transaction', () => { + showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}]); + expect(datagrid.create_transaction).not.toHaveBeenCalled(); + }); + + it('display alert', () => { + showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}]); + expect(alertify.alert).toHaveBeenCalledWith( + 'Data Grid Error', + 'No object selected.' + ); + }); + }); + + context('current node is not underneath a server', () => { + it('does not create a transaction', () => { + showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'parent'}]); + expect(datagrid.create_transaction).not.toHaveBeenCalled(); + }); + }); + + context('current node is not underneath a schema or view or catalog', () => { + it('does not create a transaction', () => { + showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'database1'}]); + expect(datagrid.create_transaction).not.toHaveBeenCalled(); + }); + }); + + context('current node is underneath a schema', () => { + it('does not create a transaction', () => { + showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'schema1'}]); + expect(datagrid.create_transaction).toHaveBeenCalledWith( + '/initialize/datagrid/11/schema/1/2/3/4', + null, + 'false', + 'pg', + '', + 'server1 - database1 - schema1.schema1', + '' + ); + }); + }); + + context('current node is underneath a view', () => { + it('does not create a transaction', () => { + showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'view1'}]); + expect(datagrid.create_transaction).toHaveBeenCalledWith( + '/initialize/datagrid/11/view/1/2/3/5', + null, + 'false', + 'pg', + '', + 'server1 - database1 - view1.view1', + '' + ); + }); + }); + + context('current node is underneath a catalog', () => { + it('does not create a transaction', () => { + showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'catalog1'}]); + expect(datagrid.create_transaction).toHaveBeenCalledWith( + '/initialize/datagrid/11/catalog/1/2/3/6', + null, + 'false', + 'pg', + '', + 'server1 - database1 - catalog1.catalog1', + '' + ); + }); + }); +}); diff --git a/web/regression/javascript/datagrid/show_query_tool_spec.js b/web/regression/javascript/datagrid/show_query_tool_spec.js new file mode 100644 index 00000000..66bd37ce --- /dev/null +++ b/web/regression/javascript/datagrid/show_query_tool_spec.js @@ -0,0 +1,125 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2018, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import {TreeFake} from '../tree/tree_fake'; +import {showQueryTool} from '../../../pgadmin/tools/datagrid/static/js/show_query_tool'; +import {TreeNode} from '../../../pgadmin/static/js/tree/tree'; + +const context = describe; + +describe('#showQueryTool', () => { + let queryTool; + let pgBrowser; + let alertify; + beforeEach(() => { + alertify = jasmine.createSpyObj('alertify', ['alert']); + queryTool = { + create_transaction: jasmine.createSpy('create_transaction'), + }; + pgBrowser = { + treeMenu: new TreeFake(), + Nodes: { + server_group: { + _type: 'server_group', + hasId: true, + }, + server: { + _type: 'server', + hasId: true, + }, + database: { + _type: 'database', + hasId: true, + }, + }, + }; + const parent = pgBrowser.treeMenu.addNewNode('parent', {_type: 'parent'}); + const serverGroup1 = new TreeNode('server_group1', { + _type: 'server_group', + _id: 1, + }, ['parent']); + pgBrowser.treeMenu.addChild(parent, serverGroup1); + + const server1 = new TreeNode('server1', { + _type: 'server', + label: 'server1', + server_type: 'pg', + _id: 2, + }); + pgBrowser.treeMenu.addChild(serverGroup1, server1); + + const database1 = new TreeNode('database1', { + _type: 'database', + label: 'database1', + _id: 3, + }); + pgBrowser.treeMenu.addChild(server1, database1); + }); + + context('cannot find the tree node', () => { + beforeEach(() => { + showQueryTool(queryTool, pgBrowser, alertify, '', [{id: '10'}], 'title'); + }); + it('does not create a transaction', () => { + expect(queryTool.create_transaction).not.toHaveBeenCalled(); + }); + + it('display alert', () => { + expect(alertify.alert).toHaveBeenCalledWith( + 'Query Tool Error', + 'No object selected.' + ); + }); + }); + + context('current node is not underneath a server', () => { + it('does not create a transaction', () => { + showQueryTool(queryTool, pgBrowser, alertify, '', [{id: 'parent'}], 'title'); + expect(queryTool.create_transaction).not.toHaveBeenCalled(); + }); + + it('no alert is displayed', () => { + expect(alertify.alert).not.toHaveBeenCalled(); + }); + }); + + context('current node is underneath a server', () => { + context('current node is not underneath a database', () => { + it('creates a transaction', () => { + showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'server1'}], 'title'); + expect(queryTool.create_transaction).toHaveBeenCalledWith( + '/initialize/query_tool/1/2', + null, + 'true', + 'pg', + 'http://someurl', + 'title', + '', + false + ); + }); + }); + + context('current node is underneath a database', () => { + it('creates a transaction', () => { + showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'database1'}], 'title'); + expect(queryTool.create_transaction).toHaveBeenCalledWith( + '/initialize/query_tool/1/2/3', + null, + 'true', + 'pg', + 'http://someurl', + 'title', + '', + false + ); + }); + }); + }); +}); diff --git a/web/regression/javascript/fake_endpoints.js b/web/regression/javascript/fake_endpoints.js index 54b86a94..c060ba78 100644 --- a/web/regression/javascript/fake_endpoints.js +++ b/web/regression/javascript/fake_endpoints.js @@ -12,5 +12,11 @@ define(function () { 'static': '/base/pgadmin/static/