2019-01-02 04:24:12 -06:00
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
2020-01-02 08:43:50 -06:00
// Copyright (C) 2013 - 2020, The pgAdmin Development Team
2019-01-02 04:24:12 -06:00
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
2018-01-12 01:29:51 -06:00
define ( 'pgadmin.browser.node' , [
2018-10-08 04:03:19 -05:00
'sources/tree/pgadmin_tree_node' , 'sources/url_for' ,
2019-10-10 01:35:28 -05:00
'sources/gettext' , 'jquery' , 'underscore' , 'sources/pgadmin' ,
2018-01-12 01:29:51 -06:00
'pgadmin.browser.menu' , 'backbone' , 'pgadmin.alertifyjs' , 'pgadmin.browser.datamodel' ,
2019-03-22 09:09:24 -05:00
'backform' , 'sources/browser/generate_url' , 'pgadmin.help' , 'sources/utils' ,
'pgadmin.browser.utils' , 'pgadmin.backform' ,
2018-06-05 05:36:19 -05:00
] , function (
2018-10-08 04:03:19 -05:00
pgadminTreeNode , url _for ,
2019-10-10 01:35:28 -05:00
gettext , $ , _ , pgAdmin ,
2019-03-22 09:09:24 -05:00
Menu , Backbone , Alertify , pgBrowser ,
Backform , generateUrl , help ,
commonUtils
2018-06-05 05:36:19 -05:00
) {
2015-06-30 00:51:55 -05:00
2017-06-12 10:43:29 -05:00
var wcDocker = window . wcDocker ,
keyCode = {
ENTER : 13 ,
ESCAPE : 27 ,
2018-01-12 01:29:51 -06:00
F1 : 112 ,
2017-06-12 10:43:29 -05:00
} ;
2015-06-30 00:51:55 -05:00
// It has already been defined.
// Avoid running this script again.
if ( pgBrowser . Node )
return pgBrowser . Node ;
pgBrowser . Nodes = pgBrowser . Nodes || { } ;
// A helper (base) class for all the nodes, this has basic
// operations/callbacks defined for basic operation.
pgBrowser . Node = function ( ) { } ;
// Helper function to correctly set up the property chain, for subclasses.
// Uses a hash of class properties to be extended.
//
// It is unlikely - we will instantiate an object for this class.
// (Inspired by Backbone.extend function)
2018-06-05 05:36:19 -05:00
pgBrowser . Node . extend = function ( props , initialize ) {
2015-06-30 00:51:55 -05:00
var parent = this ;
var child ;
// The constructor function for the new subclass is defined to simply call
// the parent's constructor.
2018-01-12 01:29:51 -06:00
child = function ( ) {
return parent . apply ( this , arguments ) ;
} ;
2015-06-30 00:51:55 -05:00
// Add static properties to the constructor function, if supplied.
_ . extend ( child , parent , _ . omit ( props , 'callbacks' ) ) ;
// Make sure - a child have all the callbacks of the parent.
child . callbacks = _ . extend ( { } , parent . callbacks , props . callbacks ) ;
2018-06-05 05:36:19 -05:00
// Let's not bind the callbacks, or initialize the child.
if ( initialize === false )
return child ;
2016-08-29 01:22:50 -05:00
var bindToChild = function ( cb ) {
2018-01-12 01:29:51 -06:00
if ( typeof ( child . callbacks [ cb ] ) == 'function' ) {
child . callbacks [ cb ] = child . callbacks [ cb ] . bind ( child ) ;
}
} ,
callbacks = _ . keys ( child . callbacks ) ;
for ( var idx = 0 ; idx < callbacks . length ; idx ++ ) bindToChild ( callbacks [ idx ] ) ;
2016-08-29 01:22:50 -05:00
2015-06-30 00:51:55 -05:00
// Registering the node by calling child.Init(...) function
child . Init . apply ( child ) ;
// Initialize the parent
this . Init . apply ( child ) ;
return child ;
} ;
2015-11-19 12:11:58 -06:00
_ . extend ( pgAdmin . Browser . Node , Backbone . Events , {
2015-06-30 00:51:55 -05:00
// Node type
type : undefined ,
// Label
label : '' ,
2016-05-16 10:25:51 -05:00
// Help pages
2016-04-12 07:35:47 -05:00
sqlAlterHelp : '' ,
sqlCreateHelp : '' ,
2016-05-16 10:25:51 -05:00
dialogHelp : '' ,
2016-04-12 07:35:47 -05:00
2017-07-18 09:13:16 -05:00
title : function ( o , d ) {
2016-01-11 10:00:42 -06:00
return o . label + ( d ? ( ' - ' + d . label ) : '' ) ;
2015-06-30 00:51:55 -05:00
} ,
2015-11-19 11:45:48 -06:00
hasId : true ,
2015-06-30 00:51:55 -05:00
///////
// Initialization function
// Generally - used to register the menus for this type of node.
//
// Also, look at pgAdmin.Browser.add_menus(...) function.
//
// NOTE: Override this for each node for initialization purpose
Init : function ( ) {
2016-01-04 10:27:18 -06:00
var self = this ;
if ( self . node _initialized )
2015-06-30 00:51:55 -05:00
return ;
2016-01-04 10:27:18 -06:00
self . node _initialized = true ;
2015-06-30 00:51:55 -05:00
pgAdmin . Browser . add _menus ( [ {
2018-01-12 01:29:51 -06:00
name : 'refresh' ,
node : self . type ,
module : self ,
applies : [ 'object' , 'context' ] ,
callback : 'refresh' ,
priority : 1 ,
label : gettext ( 'Refresh...' ) ,
2020-08-13 01:34:00 -05:00
icon : 'fa fa-sync-alt' ,
2015-06-30 00:51:55 -05:00
} ] ) ;
2016-01-04 10:27:18 -06:00
2016-08-22 06:30:16 -05:00
if ( self . canEdit ) {
pgAdmin . Browser . add _menus ( [ {
2018-01-12 01:29:51 -06:00
name : 'show_obj_properties' ,
node : self . type ,
module : self ,
applies : [ 'object' , 'context' ] ,
callback : 'show_obj_properties' ,
priority : 999 ,
label : gettext ( 'Properties...' ) ,
data : {
'action' : 'edit' ,
} ,
2020-08-13 01:34:00 -05:00
icon : 'fa fa-edit' ,
2020-10-12 03:19:54 -05:00
enable : _ . isFunction ( self . canEdit ) ?
function ( ) {
return ! ! ( self . canEdit . apply ( self , arguments ) ) ;
} : ( ! ! self . canEdit ) ,
2018-01-12 01:29:51 -06:00
} ] ) ;
2016-08-22 06:30:16 -05:00
}
2016-01-04 10:27:18 -06:00
if ( self . canDrop ) {
pgAdmin . Browser . add _menus ( [ {
2018-01-12 01:29:51 -06:00
name : 'delete_object' ,
node : self . type ,
module : self ,
applies : [ 'object' , 'context' ] ,
callback : 'delete_obj' ,
2019-12-03 00:22:02 -06:00
priority : self . dropPriority ,
label : ( self . dropAsRemove ) ? gettext ( 'Remove %s' , self . label ) : gettext ( 'Delete/Drop' ) ,
2018-01-12 01:29:51 -06:00
data : {
'url' : 'drop' ,
} ,
2020-08-21 03:45:18 -05:00
icon : 'fa fa-trash-alt' ,
2016-05-13 14:39:59 -05:00
enable : _ . isFunction ( self . canDrop ) ?
function ( ) {
return ! ! ( self . canDrop . apply ( self , arguments ) ) ;
2018-01-12 01:29:51 -06:00
} : ( ! ! self . canDrop ) ,
2016-01-04 10:27:18 -06:00
} ] ) ;
2018-01-12 01:29:51 -06:00
2016-01-04 10:27:18 -06:00
if ( self . canDropCascade ) {
pgAdmin . Browser . add _menus ( [ {
2018-01-12 01:29:51 -06:00
name : 'delete_object_cascade' ,
node : self . type ,
module : self ,
applies : [ 'object' , 'context' ] ,
callback : 'delete_obj' ,
priority : 3 ,
label : gettext ( 'Drop Cascade' ) ,
data : {
'url' : 'delete' ,
} ,
2020-08-21 03:45:18 -05:00
icon : 'fa fa-trash-alt' ,
2016-05-06 09:47:21 -05:00
enable : _ . isFunction ( self . canDropCascade ) ?
2018-01-12 01:29:51 -06:00
function ( ) {
return self . canDropCascade . apply ( self , arguments ) ;
} : ( ! ! self . canDropCascade ) ,
2016-01-04 10:27:18 -06:00
} ] ) ;
}
}
2016-06-08 07:18:59 -05:00
2018-01-12 01:29:51 -06:00
// Show query tool only in context menu of supported nodes.
if ( _ . indexOf ( pgAdmin . unsupported _nodes , self . type ) == - 1 ) {
2020-04-06 07:03:07 -05:00
let enable = function ( itemData ) {
if ( itemData . _type == 'database' && itemData . allowConn )
return true ;
else if ( itemData . _type != 'database' )
return true ;
else
return false ;
} ;
2018-01-12 01:29:51 -06:00
pgAdmin . Browser . add _menus ( [ {
name : 'show_query_tool' ,
node : self . type ,
module : self ,
applies : [ 'context' ] ,
callback : 'show_query_tool' ,
priority : 998 ,
label : gettext ( 'Query Tool...' ) ,
2019-12-06 07:57:55 -06:00
icon : 'pg-font-icon icon-query-tool' ,
2020-04-06 07:03:07 -05:00
enable : enable ,
} ] ) ;
// show search objects same as query tool
pgAdmin . Browser . add _menus ( [ {
name : 'search_objects' , node : self . type , module : pgAdmin . SearchObjects ,
applies : [ 'context' ] , callback : 'show_search_objects' ,
priority : 997 , label : gettext ( 'Search Objects...' ) ,
icon : 'fa fa-search' , enable : enable ,
2018-01-12 01:29:51 -06:00
} ] ) ;
2016-06-08 07:18:59 -05:00
}
2016-05-15 13:55:31 -05:00
// This will add options of scripts eg:'CREATE Script'
2018-01-12 01:29:51 -06:00
if ( self . hasScriptTypes && _ . isArray ( self . hasScriptTypes ) &&
self . hasScriptTypes . length > 0 ) {
// For each script type create menu
_ . each ( self . hasScriptTypes , function ( stype ) {
2019-10-10 01:35:28 -05:00
var type _label = gettext ( '%s Script' , stype . toUpperCase ( ) ) ;
2018-01-12 01:29:51 -06:00
stype = stype . toLowerCase ( ) ;
// Adding menu for each script type
pgAdmin . Browser . add _menus ( [ {
name : 'show_script_' + stype ,
node : self . type ,
module : self ,
applies : [ 'object' , 'context' ] ,
callback : 'show_script' ,
priority : 4 ,
label : type _label ,
2020-03-24 00:44:05 -05:00
category : gettext ( 'Scripts' ) ,
2018-01-12 01:29:51 -06:00
data : {
'script' : stype ,
} ,
2020-08-13 01:34:00 -05:00
icon : 'fa fa-pencil-alt' ,
2018-01-12 01:29:51 -06:00
enable : self . check _user _permission ,
} ] ) ;
} ) ;
2016-05-15 13:55:31 -05:00
}
} ,
///////
// Checks if Script Type is allowed to user
// First check if role node & create role allowed
// Otherwise test rest of database objects
// if no permission matched then do not allow create script
///////
check _user _permission : function ( itemData , item , data ) {
2016-09-23 04:06:50 -05:00
// Do not display CREATE script on server group and server node
2017-07-27 06:55:07 -05:00
if ( itemData . _type == 'server_group' || itemData . _type == 'server' ) {
2016-09-23 04:06:50 -05:00
return false ;
}
2016-12-09 05:59:13 -06:00
// Do not display the menu if the database connection is not allowed
if ( itemData . _type == 'database' && ! itemData . allowConn )
return false ;
2016-05-15 13:55:31 -05:00
var node = pgBrowser . Nodes [ itemData . _type ] ,
parentData = node . getTreeNodeHierarchy ( item ) ;
2018-01-12 01:29:51 -06:00
if ( _ . indexOf ( [ 'create' , 'insert' , 'update' , 'delete' ] , data . script ) != - 1 ) {
2016-05-15 13:55:31 -05:00
if ( itemData . type == 'role' &&
parentData . server . user . can _create _role ) {
return true ;
2016-10-18 06:37:44 -05:00
} else if (
2018-01-12 01:29:51 -06:00
(
2016-10-18 06:37:44 -05:00
parentData . server && (
2018-01-12 01:29:51 -06:00
parentData . server . user . is _superuser ||
parentData . server . user . can _create _db )
) ||
(
2016-10-18 06:37:44 -05:00
parentData . schema && parentData . schema . can _create
2018-01-12 01:29:51 -06:00
)
) {
return true ;
2016-05-15 13:55:31 -05:00
} else {
2018-01-12 01:29:51 -06:00
return false ;
2016-05-15 13:55:31 -05:00
}
} else {
return true ;
}
2015-06-30 00:51:55 -05:00
} ,
///////
// Generate a Backform view using the node's model type
//
// Used to generate view for the particular node properties, edit,
// creation.
2016-08-29 01:22:50 -05:00
getView : function ( item , type , el , node , formType , callback , ctx , cancelFunc ) {
2016-01-04 02:04:45 -06:00
var that = this ;
2015-06-30 00:51:55 -05:00
2015-10-28 12:06:09 -05:00
if ( ! this . type || this . type == '' )
2015-06-30 00:51:55 -05:00
// We have no information, how to generate view for this type.
return null ;
if ( this . model ) {
// This will be the URL, used for object manipulation.
// i.e. Create, Update in these cases
2020-01-01 01:29:48 -06:00
var urlBase = this . generate _url ( item , type , node , false , null , that . url _jump _after _node ) ;
2015-06-30 00:51:55 -05:00
if ( ! urlBase )
// Ashamed of myself, I don't know how to manipulate this
// node.
return null ;
2015-10-28 12:06:09 -05:00
var attrs = { } ;
2015-06-30 00:51:55 -05:00
// In order to get the object data from the server, we must set
// object-id in the model (except in the create mode).
if ( type !== 'create' ) {
2015-12-16 02:07:49 -06:00
attrs [ this . model . idAttribute || this . model . prototype . idAttribute ||
'id' ] = node . _id ;
2015-06-30 00:51:55 -05:00
}
// We know - which data model to be used for this object.
2016-01-15 07:40:29 -06:00
var info = this . getTreeNodeHierarchy . apply ( this , [ item ] ) ,
2018-01-12 01:29:51 -06:00
newModel = new ( this . model . extend ( {
urlRoot : urlBase ,
} ) ) (
attrs , {
node _info : info ,
}
) ,
fields = Backform . generateViewSchema (
info , newModel , type , this , node
) ;
2015-06-30 00:51:55 -05:00
2016-01-04 05:24:06 -06:00
if ( type == 'create' || type == 'edit' ) {
2016-01-12 10:48:54 -06:00
if ( callback && ctx ) {
2018-01-12 01:29:51 -06:00
callback = callback . bind ( ctx ) ;
2016-01-12 10:48:54 -06:00
} else {
callback = function ( ) {
2018-01-12 01:29:51 -06:00
console . warn (
'Broke something!!! Why we don\'t have the callback or the context???'
) ;
2016-01-12 10:48:54 -06:00
} ;
}
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
var onSessionInvalid = function ( msg ) {
2019-01-02 03:35:15 -06:00
var alertMessage = `
< div class = "error-in-footer" >
< div class = "d-flex px-2 py-1" >
< div class = "pr-2" >
2019-12-17 01:52:36 -06:00
< i class = "fa fa-exclamation-triangle text-danger" aria - hidden = "true" role = "img" > < / i >
2019-01-02 03:35:15 -06:00
< / d i v >
2020-01-28 00:02:11 -06:00
< div role = "alert" class = "alert-text" > $ { msg } < / d i v >
2019-01-02 03:35:15 -06:00
< div class = "ml-auto close-error-bar" >
< a class = "close-error fa fa-times text-danger" > < / a >
< / d i v >
< / d i v >
< / d i v > ` ;
2018-01-12 01:29:51 -06:00
if ( ! _ . isUndefined ( that . statusBar ) ) {
that . statusBar . html ( alertMessage ) . css ( 'visibility' , 'visible' ) ;
that . statusBar . find ( 'a.close-error' ) . bind ( 'click' , function ( ) {
this . empty ( ) . css ( 'visibility' , 'hidden' ) ;
2017-11-28 08:10:12 -06:00
} . bind ( that . statusBar ) ) ;
2016-01-04 05:24:06 -06:00
}
2019-11-15 00:21:06 -06:00
var sessHasChanged = false ;
if ( this . sessChanged && this . sessChanged ( ) ) {
sessHasChanged = true ;
}
callback ( true , sessHasChanged ) ;
2016-01-12 10:48:54 -06:00
return true ;
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
} ;
2016-01-12 10:48:54 -06:00
2018-01-12 01:29:51 -06:00
var onSessionValidated = function ( sessHasChanged ) {
2016-01-12 10:48:54 -06:00
2018-01-12 01:29:51 -06:00
if ( ! _ . isUndefined ( that . statusBar ) ) {
that . statusBar . empty ( ) . css ( 'visibility' , 'hidden' ) ;
2016-01-04 05:24:06 -06:00
}
2016-01-12 10:48:54 -06:00
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
callback ( false , sessHasChanged ) ;
} ;
2016-01-12 10:48:54 -06:00
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
callback ( false , false ) ;
newModel . on ( 'pgadmin-session:valid' , onSessionValidated ) ;
newModel . on ( 'pgadmin-session:invalid' , onSessionInvalid ) ;
2016-01-04 02:04:45 -06:00
}
2015-06-30 00:51:55 -05:00
// 'schema' has the information about how to generate the form.
2016-01-17 10:51:02 -06:00
if ( _ . size ( fields ) ) {
2015-06-30 00:51:55 -05:00
// This will contain the actual view
var view ;
if ( formType == 'fieldset' ) {
// It is used to show, edit, create the object in the
// properties tab.
2019-01-03 08:50:24 -06:00
view = new Backform . Accordian ( {
2018-01-12 01:29:51 -06:00
el : el ,
model : newModel ,
schema : fields ,
2015-06-30 00:51:55 -05:00
} ) ;
} else {
// This generates a view to be used by the node dialog
// (for create/edit operation).
view = new Backform . Dialog ( {
2018-01-12 01:29:51 -06:00
el : el ,
model : newModel ,
schema : fields ,
2015-06-30 00:51:55 -05:00
} ) ;
}
2017-06-08 08:37:31 -05:00
var setFocusOnEl = function ( ) {
2018-02-27 05:18:36 -06:00
var container = $ ( el ) . find ( '.tab-content:first > .tab-pane.active:first' ) ;
commonUtils . findAndSetFocus ( container ) ;
2017-06-08 08:37:31 -05:00
} ;
2015-06-30 00:51:55 -05:00
if ( ! newModel . isNew ( ) ) {
// This is definetely not in create mode
2020-02-24 02:41:00 -06:00
var msgDiv = '<div role="status" class="pg-panel-message pg-panel-properties-message">' +
2018-01-12 01:29:51 -06:00
gettext ( 'Retrieving data from the server...' ) + '</div>' ,
$msgDiv = $ ( msgDiv ) ;
2020-07-14 05:15:01 -05:00
var timer = setTimeout ( function ( _ctx ) {
2016-09-26 09:10:38 -05:00
// notify user if request is taking longer than 1 second
2020-07-14 05:15:01 -05:00
if ( ! _ . isUndefined ( _ctx ) ) {
$msgDiv . appendTo ( _ctx ) ;
2016-09-26 09:10:38 -05:00
}
} , 1000 , ctx ) ;
2017-06-08 08:37:31 -05:00
2019-05-28 01:30:18 -05:00
var fetchAjaxHook = function ( ) {
newModel . fetch ( {
success : function ( ) {
// Clear timeout and remove message
clearTimeout ( timer ) ;
$msgDiv . addClass ( 'd-none' ) ;
// We got the latest attributes of the object. Render the view
// now.
view . render ( ) ;
setFocusOnEl ( ) ;
newModel . startNewSession ( ) ;
} ,
error : function ( model , xhr , options ) {
var _label = that && item ?
that . getTreeNodeHierarchy (
item
) [ that . type ] . label : '' ;
pgBrowser . Events . trigger (
'pgadmin:node:retrieval:error' , 'properties' ,
xhr , options . textStatus , options . errorThrown , item
2017-07-18 09:13:16 -05:00
) ;
2019-05-28 01:30:18 -05:00
if ( ! Alertify . pgHandleItemError (
xhr , options . textStatus , options . errorThrown , {
item : item ,
info : info ,
}
) ) {
Alertify . pgNotifier (
options . textStatus , xhr ,
2019-10-10 01:35:28 -05:00
gettext ( 'Error retrieving properties - %s' , options . errorThrown || _label ) ,
function ( msg ) {
2019-05-28 01:30:18 -05:00
if ( msg === 'CRYPTKEY_SET' ) {
fetchAjaxHook ( ) ;
} else {
console . warn ( arguments ) ;
}
}
) ;
}
// Close the panel (if could not fetch properties)
if ( cancelFunc ) {
cancelFunc ( ) ;
}
} ,
} ) ;
} ;
fetchAjaxHook ( ) ;
2015-06-30 00:51:55 -05:00
} else {
// Yay - render the view now!
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
view . render ( ) ;
2017-06-08 08:37:31 -05:00
setFocusOnEl ( ) ;
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
newModel . startNewSession ( ) ;
2015-06-30 00:51:55 -05:00
}
}
2018-02-27 05:18:36 -06:00
2015-06-30 00:51:55 -05:00
return view ;
}
return null ;
} ,
register _node _panel : function ( ) {
var w = pgBrowser . docker ,
p = w . findPanels ( 'node_props' ) ;
if ( p && p . length == 1 )
return ;
2016-09-26 04:04:49 -05:00
var events = { } ;
events [ wcDocker . EVENT . RESIZE _ENDED ] = function ( ) {
var $container = this . $container . find ( '.obj_properties' ) . first ( ) ,
2018-01-12 01:29:51 -06:00
v = $container . data ( 'obj-view' ) ;
2016-09-26 04:04:49 -05:00
if ( v && v . model && v . model ) {
v . model . trigger (
'pg-browser-resized' , {
2018-01-12 01:29:51 -06:00
'view' : v ,
'panel' : this ,
'container' : $container ,
} ) ;
2016-09-26 04:04:49 -05:00
}
} ;
2015-06-30 00:51:55 -05:00
p = new pgBrowser . Panel ( {
2018-01-12 01:29:51 -06:00
name : 'node_props' ,
showTitle : true ,
isCloseable : true ,
isPrivate : true ,
2019-03-26 10:08:45 -05:00
isLayoutMember : false ,
2018-01-12 01:29:51 -06:00
elContainer : true ,
2020-02-24 02:41:00 -06:00
content : '<div class="obj_properties container-fluid"><div role="status" class="pg-panel-message">' + gettext ( 'Please wait while we fetch information about the node from the server...' ) + '</div></div>' ,
2018-01-12 01:29:51 -06:00
onCreate : function ( myPanel , $container ) {
$container . addClass ( 'pg-no-overflow' ) ;
} ,
events : events ,
2016-09-26 04:04:49 -05:00
} ) ;
2015-06-30 00:51:55 -05:00
p . load ( pgBrowser . docker ) ;
} ,
2016-08-22 06:30:16 -05:00
/ *
* Default script type menu for node .
*
* Override this , to show more script type menus ( e . g hasScriptTypes : [ 'create' , 'select' , 'insert' , 'update' , 'delete' ] )
*
* Or set it to empty array to disable script type menu on node ( e . g hasScriptTypes : [ ] )
* /
hasScriptTypes : [ 'create' ] ,
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This function determines the given item is editable or not .
*
* Override this , when a node is not editable .
* /
canEdit : true ,
2015-06-30 00:51:55 -05:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This function determines the given item is deletable or not .
*
* Override this , when a node is not deletable .
* /
2016-01-04 10:27:18 -06:00
canDrop : false ,
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This function determines the given item and children are deletable or
* not .
*
* Override this , when a node is not deletable .
* /
canDropCascade : false ,
2019-12-03 00:22:02 -06:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
dropAsRemove should be true in case , Drop object label needs to be replaced by Remove
* /
dropAsRemove : false ,
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
dropPriority is set to 2 by default , override it when change is required
* /
dropPriority : 2 ,
2015-06-30 00:51:55 -05:00
// List of common callbacks - that can be used for different
// operations!
callbacks : {
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This function allows to create / edit / show properties of any
* object depending on the arguments provided .
*
* args must be a object containing :
* action - create / edit / properties
2017-11-07 19:27:10 -06:00
* item - The properties of the item ( tree node item )
2015-06-30 00:51:55 -05:00
*
* NOTE :
* if item is not provided , the action will be done on the
* currently selected tree item node .
*
* * /
2016-01-06 21:07:14 -06:00
show _obj _properties : function ( args , item ) {
2015-06-30 00:51:55 -05:00
var t = pgBrowser . tree ,
2018-02-09 06:07:57 -06:00
i = ( args && args . item ) || item || t . selected ( ) ,
2017-07-18 09:13:16 -05:00
d = i && i . length == 1 ? t . itemData ( i ) : undefined ,
2015-06-30 00:51:55 -05:00
o = this ,
2016-01-11 10:00:42 -06:00
l = o . title . apply ( this , [ d ] ) ;
2015-06-30 00:51:55 -05:00
// Make sure - the properties dialog type registered
pgBrowser . Node . register _node _panel ( ) ;
// No node selected.
if ( ! d )
return ;
2016-01-04 06:22:30 -06:00
var self = this ,
2018-01-12 01:29:51 -06:00
isParent = ( _ . isArray ( this . parent _type ) ?
2020-07-14 05:15:01 -05:00
function ( _d ) {
return ( _ . indexOf ( self . parent _type , _d . _type ) != - 1 ) ;
} : function ( _d ) {
return ( self . parent _type == _d . _type ) ;
2018-01-12 01:29:51 -06:00
} ) ,
addPanel = function ( ) {
2020-07-14 05:15:01 -05:00
var body = window . document . body ,
el = document . createElement ( 'div' ) ;
2018-01-12 01:29:51 -06:00
2020-07-14 05:15:01 -05:00
body . insertBefore ( el , body . firstChild ) ;
2018-01-12 01:29:51 -06:00
2019-01-02 03:35:15 -06:00
let w , h , x , y ;
if ( screen . width < 800 ) {
2020-07-01 01:19:39 -05:00
w = pgAdmin . toPx ( el , '95%' , 'width' , true ) ;
2019-01-02 03:35:15 -06:00
} else {
2020-07-01 01:19:39 -05:00
w = pgAdmin . toPx (
2020-07-03 00:56:06 -05:00
el , self . width || ( pgBrowser . stdW . default + 'px' ) ,
2020-07-01 01:19:39 -05:00
'width' , true
) ;
2019-01-02 03:35:15 -06:00
/* Fit to standard sizes */
if ( w <= pgBrowser . stdW . sm ) {
w = pgBrowser . stdW . sm ;
} else {
if ( w <= pgBrowser . stdW . md ) {
w = pgBrowser . stdW . md ;
} else {
w = pgBrowser . stdW . lg ;
}
}
}
if ( screen . height < 600 ) {
h = pgAdmin . toPx ( el , '95%' , 'height' , true ) ;
} else {
2020-07-01 01:19:39 -05:00
h = pgAdmin . toPx (
2020-07-03 00:56:06 -05:00
el , self . height || ( pgBrowser . stdH . default + 'px' ) ,
2020-07-01 01:19:39 -05:00
'height' , true
) ;
2019-01-02 03:35:15 -06:00
/* Fit to standard sizes */
if ( h <= pgBrowser . stdH . sm ) {
h = pgBrowser . stdH . sm ;
} else {
if ( h <= pgBrowser . stdH . md ) {
h = pgBrowser . stdH . md ;
} else {
h = pgBrowser . stdH . lg ;
}
}
}
2020-07-14 05:15:01 -05:00
x = ( body . offsetWidth - w ) / 2 ;
y = ( body . offsetHeight - h ) / 4 ;
2018-01-12 01:29:51 -06:00
2020-07-01 01:19:39 -05:00
// If the screen resolution is higher, but - it is zoomed, dialog
// may be go out of window, and will not be accessible through the
// keyboard.
if ( w > window . innerWidth ) {
x = 0 ;
w = window . innerWidth ;
}
if ( h > window . innerHeight ) {
y = 0 ;
h = window . innerHeight ;
}
2020-07-14 05:15:01 -05:00
var new _panel = pgBrowser . docker . addPanel (
2018-01-12 01:29:51 -06:00
'node_props' , wcDocker . DOCK . FLOAT , undefined , {
w : w + 'px' ,
h : h + 'px' ,
x : x + 'px' ,
y : y + 'px' ,
}
) ;
2016-05-21 03:48:24 -05:00
2020-07-14 05:15:01 -05:00
body . removeChild ( el ) ;
2016-05-21 03:48:24 -05:00
2020-07-14 05:15:01 -05:00
return new _panel ;
2018-01-12 01:29:51 -06:00
} ;
2016-01-04 06:22:30 -06:00
2015-06-30 00:51:55 -05:00
if ( args . action == 'create' ) {
// If we've parent, we will get the information of it for
// proper object manipulation.
//
// You know - we're working with RDBMS, relation is everything
// for us.
2016-01-04 11:55:21 -06:00
if ( self . parent _type && ! isParent ( d ) ) {
2015-06-30 00:51:55 -05:00
// In browser tree, I can be under any node, But - that
// does not mean, it is my parent.
//
// We have some group nodes too.
//
// i.e.
// Tables, Views, etc. nodes under Schema node
//
// And, actual parent of a table is schema, not Tables.
while ( i && t . hasParent ( i ) ) {
i = t . parent ( i ) ;
2017-07-18 09:13:16 -05:00
var pd = t . itemData ( i ) ;
2015-06-30 00:51:55 -05:00
2016-01-04 11:55:21 -06:00
if ( isParent ( pd ) ) {
2015-06-30 00:51:55 -05:00
// Assign the data, this is my actual parent.
d = pd ;
break ;
}
}
}
// Seriously - I really don't have parent data present?
//
// The only node - which I know - who does not have parent
// node, is the Server Group (and, comes directly under root
// node - which has no parent.)
2016-01-04 11:55:21 -06:00
if ( ! d || ( this . parent _type != null && ! isParent ( d ) ) ) {
2015-06-30 00:51:55 -05:00
// It should never come here.
// If it is here, that means - we do have some bug in code.
return ;
}
2019-10-10 01:35:28 -05:00
l = gettext ( 'Create - %s' , this . label ) ;
2016-05-21 03:48:24 -05:00
p = addPanel ( ) ;
2015-06-30 00:51:55 -05:00
setTimeout ( function ( ) {
o . showProperties ( i , d , p , args . action ) ;
} , 10 ) ;
} else {
if ( pgBrowser . Node . panels && pgBrowser . Node . panels [ d . id ] &&
2018-01-12 01:29:51 -06:00
pgBrowser . Node . panels [ d . id ] . $container ) {
2017-07-18 09:13:16 -05:00
var p = pgBrowser . Node . panels [ d . id ] ;
2015-06-30 00:51:55 -05:00
/ * * T O D O : :
* Run in edit mode ( if asked ) only when it is
* not already been running edit mode
* * /
var mode = p . $container . attr ( 'action-mode' ) ;
if ( mode ) {
2017-06-07 05:23:02 -05:00
var msg = gettext ( 'Are you sure want to stop editing the properties of %s "%s"?' ) ;
2015-06-30 00:51:55 -05:00
if ( args . action == 'edit' ) {
2017-06-07 05:23:02 -05:00
msg = gettext ( 'Are you sure want to reset the current changes and re-open the panel for %s "%s"?' ) ;
2015-06-30 00:51:55 -05:00
}
Alertify . confirm (
2017-06-07 05:23:02 -05:00
gettext ( 'Edit in progress?' ) ,
2019-10-10 01:35:28 -05:00
commonUtils . sprintf ( msg , o . label . toLowerCase ( ) , d . label ) ,
2015-06-30 00:51:55 -05:00
function ( ) {
setTimeout ( function ( ) {
o . showProperties ( i , d , p , args . action ) ;
} , 10 ) ;
} ,
null ) . show ( ) ;
} else {
setTimeout ( function ( ) {
o . showProperties ( i , d , p , args . action ) ;
} , 10 ) ;
}
} else {
pgBrowser . Node . panels = pgBrowser . Node . panels || { } ;
2016-05-21 03:48:24 -05:00
p = pgBrowser . Node . panels [ d . id ] = addPanel ( ) ;
2015-06-30 00:51:55 -05:00
setTimeout ( function ( ) {
o . showProperties ( i , d , p , args . action ) ;
} , 10 ) ;
}
}
p . title ( l ) ;
p . icon ( 'icon-' + this . type ) ;
// Make sure the properties dialog is visible
p . focus ( ) ;
} ,
// Delete the selected object
2016-01-05 01:06:33 -06:00
delete _obj : function ( args , item ) {
2018-01-12 01:29:51 -06:00
var input = args || {
'url' : 'drop' ,
} ,
obj = this ,
t = pgBrowser . tree ,
i = input . item || item || t . selected ( ) ,
d = i && i . length == 1 ? t . itemData ( i ) : undefined ;
2015-06-30 00:51:55 -05:00
if ( ! d )
return ;
2016-01-05 01:06:33 -06:00
/ *
* Make sure - we ' re using the correct version of node
* /
obj = pgBrowser . Nodes [ d . _type ] ;
var objName = d . label ;
2020-04-10 04:22:41 -05:00
var msg , title ;
2019-12-03 00:22:02 -06:00
2016-01-04 10:27:18 -06:00
if ( input . url == 'delete' ) {
2019-10-10 01:35:28 -05:00
msg = gettext ( 'Are you sure you want to drop %s "%s" and all the objects that depend on it?' ,
obj . label . toLowerCase ( ) , d . label ) ;
title = gettext ( 'DROP CASCADE %s?' , obj . label ) ;
2016-01-04 10:27:18 -06:00
2016-01-05 01:06:33 -06:00
if ( ! ( _ . isFunction ( obj . canDropCascade ) ?
2019-03-14 10:11:16 -05:00
obj . canDropCascade . apply ( obj , [ d , i ] ) : obj . canDropCascade ) ) {
2018-01-12 01:29:51 -06:00
Alertify . error (
2019-10-10 01:35:28 -05:00
gettext ( 'The %s "%s" cannot be dropped.' , obj . label , d . label ) ,
2018-01-12 01:29:51 -06:00
10
) ;
2016-01-04 10:27:18 -06:00
return ;
}
} else {
2020-04-10 04:22:41 -05:00
if ( obj . dropAsRemove ) {
msg = gettext ( 'Are you sure you want to remove %s "%s"?' , obj . label . toLowerCase ( ) , d . label ) ;
title = gettext ( 'Remove %s?' , obj . label ) ;
} else {
msg = gettext ( 'Are you sure you want to drop %s "%s"?' , obj . label . toLowerCase ( ) , d . label ) ;
title = gettext ( 'Drop %s?' , obj . label ) ;
}
2015-06-30 00:51:55 -05:00
2016-01-05 01:06:33 -06:00
if ( ! ( _ . isFunction ( obj . canDrop ) ?
2019-03-14 10:11:16 -05:00
obj . canDrop . apply ( obj , [ d , i ] ) : obj . canDrop ) ) {
2019-12-03 00:22:02 -06:00
Alertify . error (
gettext ( 'The %s "%s" cannot be dropped/removed.' , obj . label , d . label ) ,
10
) ;
2016-01-04 10:27:18 -06:00
return ;
}
}
Alertify . confirm ( title , msg ,
function ( ) {
$ . ajax ( {
url : obj . generate _url ( i , input . url , d , true ) ,
2018-01-12 01:29:51 -06:00
type : 'DELETE' ,
2018-07-09 07:54:00 -05:00
} )
2019-03-14 10:11:16 -05:00
. done ( function ( res ) {
if ( res . success == 0 ) {
pgBrowser . report _error ( res . errormsg , res . info ) ;
} else {
pgBrowser . removeTreeNode ( i , true ) ;
2016-01-04 10:27:18 -06:00
}
2019-03-14 10:11:16 -05:00
return true ;
} )
. fail ( function ( jqx ) {
2020-07-14 05:15:01 -05:00
var errmsg = jqx . responseText ;
2019-03-14 10:11:16 -05:00
/* Error from the server */
if ( jqx . status == 417 || jqx . status == 410 || jqx . status == 500 ) {
try {
var data = JSON . parse ( jqx . responseText ) ;
2020-07-14 05:15:01 -05:00
errmsg = data . info || data . errormsg ;
2019-03-14 10:11:16 -05:00
} catch ( e ) {
console . warn ( e . stack || e ) ;
}
}
2019-12-03 00:22:02 -06:00
pgBrowser . report _error (
2020-07-14 05:15:01 -05:00
gettext ( 'Error dropping/removing %s: "%s"' , obj . label , objName ) , errmsg ) ;
2019-12-03 00:22:02 -06:00
2019-03-14 10:11:16 -05:00
} ) ;
2016-01-04 10:27:18 -06:00
} ,
2020-04-10 04:22:41 -05:00
null
) . set ( 'labels' , {
ok : gettext ( 'Yes' ) ,
cancel : gettext ( 'No' ) ,
} ) . show ( ) ;
2015-06-30 00:51:55 -05:00
} ,
2016-05-15 13:55:31 -05:00
// Callback for creating script(s) & opening them in Query editor
show _script : function ( args , item ) {
var scriptType = args . script ,
2020-06-10 10:42:59 -05:00
obj ,
2016-05-15 13:55:31 -05:00
t = pgBrowser . tree ,
i = item || t . selected ( ) ,
d = i && i . length == 1 ? t . itemData ( i ) : undefined ;
if ( ! d )
return ;
/ *
* Make sure - we ' re using the correct version of node
* /
obj = pgBrowser . Nodes [ d . _type ] ;
2018-01-12 01:29:51 -06:00
var sql _url ;
2016-05-15 13:55:31 -05:00
// URL for script type
2018-01-12 01:29:51 -06:00
if ( scriptType == 'insert' ) {
2016-05-15 13:55:31 -05:00
sql _url = 'insert_sql' ;
2018-01-12 01:29:51 -06:00
} else if ( scriptType == 'update' ) {
2016-05-15 13:55:31 -05:00
sql _url = 'update_sql' ;
2018-01-12 01:29:51 -06:00
} else if ( scriptType == 'delete' ) {
2016-05-15 13:55:31 -05:00
sql _url = 'delete_sql' ;
2018-01-12 01:29:51 -06:00
} else if ( scriptType == 'select' ) {
2016-05-15 13:55:31 -05:00
sql _url = 'select_sql' ;
2018-01-12 01:29:51 -06:00
} else if ( scriptType == 'exec' ) {
2016-05-15 13:55:31 -05:00
sql _url = 'exec_sql' ;
} else {
// By Default get CREATE SQL
sql _url = 'sql' ;
}
// Open data grid & pass the URL for fetching
2017-06-15 06:19:47 -05:00
pgAdmin . DataGrid . show _query _tool (
obj . generate _url ( i , sql _url , d , true ) ,
i , scriptType
2016-05-15 13:55:31 -05:00
) ;
} ,
2016-06-08 07:18:59 -05:00
// Callback to render query editor
show _query _tool : function ( args , item ) {
2018-01-12 01:29:51 -06:00
var t = pgBrowser . tree ,
2016-06-08 07:18:59 -05:00
i = item || t . selected ( ) ,
d = i && i . length == 1 ? t . itemData ( i ) : undefined ;
if ( ! d )
return ;
// Here call data grid method to render query tool
2017-06-13 05:34:13 -05:00
pgAdmin . DataGrid . show _query _tool ( '' , i ) ;
2016-06-08 07:18:59 -05:00
} ,
2017-11-21 10:28:01 -06:00
// Logic to change the server background colour
// There is no way of applying CSS to parent element so we have to
// do it via JS code only
change _server _background : function ( item , data ) {
if ( ! item || ! data )
return ;
// Go further only if node type is a Server
2020-06-18 00:44:56 -05:00
if ( data . _type && data . _type == 'server' ) {
2017-11-21 10:28:01 -06:00
var element = $ ( item ) . find ( 'span.aciTreeItem' ) . first ( ) || null ,
// First element will be icon and second will be colour code
2018-01-12 01:29:51 -06:00
bgcolor = data . icon . split ( ' ' ) [ 1 ] || null ,
fgcolor = data . icon . split ( ' ' ) [ 2 ] || '' ;
2017-11-21 10:28:01 -06:00
2018-01-12 01:29:51 -06:00
if ( bgcolor ) {
2017-11-21 10:28:01 -06:00
// li tag for the current branch
2020-06-15 05:46:57 -05:00
var first _level _element = ( element && element . parents ( ) [ 3 ] ) || null ,
2017-11-21 10:28:01 -06:00
dynamic _class = 'pga_server_' + data . _id + '_bgcolor' ,
style _tag ;
// Prepare dynamic style tag
2018-01-12 01:29:51 -06:00
style _tag = '<style id=' + dynamic _class + ' type=\'text/css\'> \n' ;
style _tag += '.' + dynamic _class + ' .aciTreeItem {' ;
style _tag += ' border-radius: 3px; margin-bottom: 2px;' ;
style _tag += ' background: ' + bgcolor + '} \n' ;
if ( fgcolor ) {
style _tag += '.' + dynamic _class + ' .aciTreeText {' ;
style _tag += ' color: ' + fgcolor + ';} \n' ;
2017-11-21 10:28:01 -06:00
}
2018-01-12 01:29:51 -06:00
style _tag += '</style>' ;
2017-11-21 10:28:01 -06:00
// Prepare dynamic style tag using template
$ ( '#' + dynamic _class ) . remove ( ) ;
2018-01-12 01:29:51 -06:00
$ ( style _tag ) . appendTo ( 'head' ) ;
2017-11-21 10:28:01 -06:00
2018-01-12 01:29:51 -06:00
if ( first _level _element )
2017-11-21 10:28:01 -06:00
$ ( first _level _element ) . addClass ( dynamic _class ) ;
}
}
} ,
2016-08-09 06:12:05 -05:00
added : function ( item , data , browser ) {
var b = browser || pgBrowser ,
2018-01-12 01:29:51 -06:00
t = b . tree ,
pItem = t . parent ( item ) ,
pData = pItem && t . itemData ( pItem ) ,
pNode = pData && pgBrowser . Nodes [ pData . _type ] ;
2016-08-09 06:12:05 -05:00
// Check node is a collection or not.
if ( pNode && pNode . is _collection ) {
/ * I f ' c o l l e c t i o n _ c o u n t ' i s n o t p r e s e n t i n d a t a
* it means tree node expanded first time , so we will
* kept collection count and label in data itself .
* /
if ( ! ( 'collection_count' in pData ) ) {
pData . collection _count = 0 ;
}
pData . collection _count ++ ;
2016-08-29 09:36:48 -05:00
t . setLabel (
pItem , {
label : (
2017-05-10 02:55:49 -05:00
_ . escape ( pData . _label ) + ' <span>(' + pData . collection _count + ')</span>'
2018-01-12 01:29:51 -06:00
) ,
2016-08-29 09:36:48 -05:00
}
) ;
2016-08-09 06:12:05 -05:00
}
2017-11-21 10:28:01 -06:00
2018-10-08 04:03:19 -05:00
pgBrowser . Events . trigger ( 'pgadmin:browser:tree:expand-from-previous-tree-state' ,
2019-03-14 10:11:16 -05:00
item ) ;
2017-11-21 10:28:01 -06:00
pgBrowser . Node . callbacks . change _server _background ( item , data ) ;
2016-08-09 06:12:05 -05:00
} ,
2015-06-30 00:51:55 -05:00
// Callback called - when a node is selected in browser tree.
2015-10-30 02:37:09 -05:00
selected : function ( item , data , browser ) {
2015-10-20 02:03:18 -05:00
// Show the information about the selected node in the below panels,
// which are visible at this time:
2015-06-30 00:51:55 -05:00
// + Properties
2015-10-20 02:03:18 -05:00
// + Query (if applicable, otherwise empty)
2015-06-30 00:51:55 -05:00
// + Dependents
// + Dependencies
// + Statistics
2015-10-30 02:37:09 -05:00
var b = browser || pgBrowser ,
2018-01-12 01:29:51 -06:00
t = b . tree ,
d = data || t . itemData ( item ) ;
2015-06-30 00:51:55 -05:00
// Update the menu items
2015-10-28 12:06:09 -05:00
pgAdmin . Browser . enable _disable _menus . apply ( b , [ item ] ) ;
if ( d && b ) {
if ( 'properties' in b . panels &&
2018-01-12 01:29:51 -06:00
b . panels [ 'properties' ] &&
b . panels [ 'properties' ] . panel &&
b . panels [ 'properties' ] . panel . isVisible ( ) ) {
2015-06-30 00:51:55 -05:00
// Show object properties (only when the 'properties' tab
// is active).
2015-10-28 12:06:09 -05:00
this . showProperties ( item , d , b . panels [ 'properties' ] . panel ) ;
2015-10-20 02:03:18 -05:00
}
2015-06-30 00:51:55 -05:00
}
2016-01-19 06:31:14 -06:00
2018-10-08 04:03:19 -05:00
pgBrowser . Events . trigger ( 'pgadmin:browser:tree:update-tree-state' ,
2019-03-14 10:11:16 -05:00
item ) ;
2016-01-19 06:31:14 -06:00
return true ;
2015-10-20 02:03:18 -05:00
} ,
2016-05-21 05:06:36 -05:00
removed : function ( item ) {
2016-08-09 06:12:05 -05:00
var self = this ,
2018-01-12 01:29:51 -06:00
t = pgBrowser . tree ,
pItem = t . parent ( item ) ,
pData = pItem && t . itemData ( pItem ) ,
pNode = pData && pgBrowser . Nodes [ pData . _type ] ;
2016-08-09 06:12:05 -05:00
// Check node is a collection or not.
2016-08-29 09:36:48 -05:00
if (
pNode && pNode . is _collection && 'collection_count' in pData
) {
2016-08-09 06:12:05 -05:00
pData . collection _count -- ;
2016-08-29 09:36:48 -05:00
t . setLabel (
pItem , {
label : (
2017-05-10 02:55:49 -05:00
_ . escape ( pData . _label ) + ' <span>(' + pData . collection _count + ')</span>'
2018-01-12 01:29:51 -06:00
) ,
2016-08-29 09:36:48 -05:00
}
) ;
2016-08-09 06:12:05 -05:00
}
2018-01-12 01:29:51 -06:00
setTimeout ( function ( ) {
self . clear _cache . apply ( self , item ) ;
} , 0 ) ;
2016-05-21 05:06:36 -05:00
} ,
2016-08-09 06:12:05 -05:00
unloaded : function ( item ) {
var self = this ,
2018-01-12 01:29:51 -06:00
t = pgBrowser . tree ,
data = item && t . itemData ( item ) ;
2016-08-09 06:12:05 -05:00
// In case of unload remove the collection counter
2020-06-15 05:46:57 -05:00
if ( self . is _collection && data === Object ( data ) && 'collection_count' in data ) {
2016-08-09 06:12:05 -05:00
delete data . collection _count ;
2018-01-12 01:29:51 -06:00
t . setLabel ( item , {
label : _ . escape ( data . _label ) ,
} ) ;
2016-08-09 06:12:05 -05:00
}
} ,
2018-01-12 01:29:51 -06:00
refresh : function ( cmd , _item ) {
2018-08-13 07:47:07 -05:00
var self = this ,
t = pgBrowser . tree ,
data = _item && t . itemData ( _item ) ;
$ ( pgBrowser . panels [ 'properties' ] . panel ) . removeData ( 'node-prop' ) ;
2018-01-12 01:29:51 -06:00
pgBrowser . Events . trigger (
2018-08-13 07:47:07 -05:00
'pgadmin:browser:tree:refresh' , _item || pgBrowser . tree . selected ( ) , {
success : function ( ) {
self . callbacks . selected . apply ( self , [ _item , data , pgBrowser ] ) ;
} ,
} ) ;
2018-01-12 01:29:51 -06:00
} ,
2018-10-08 04:03:19 -05:00
opened : function ( item ) {
2019-02-14 03:18:08 -06:00
let tree = pgBrowser . tree ,
auto _expand = pgBrowser . get _preference ( 'browser' , 'auto_expand_sole_children' ) ;
2018-10-08 04:03:19 -05:00
pgBrowser . Events . trigger ( 'pgadmin:browser:tree:update-tree-state' ,
2019-03-14 10:11:16 -05:00
item ) ;
2019-02-14 03:18:08 -06:00
if ( auto _expand && auto _expand . value == true && tree . children ( item ) . length == 1 ) {
// Automatically expand the child node, if a treeview node has only a single child.
tree . open ( tree . first ( item ) ) ;
}
2018-10-08 04:03:19 -05:00
} ,
closed : function ( item ) {
pgBrowser . Events . trigger ( 'pgadmin:browser:tree:remove-from-tree-state' ,
2019-03-14 10:11:16 -05:00
item ) ;
2018-10-08 04:03:19 -05:00
} ,
2015-06-30 00:51:55 -05:00
} ,
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* A hook ( not a callback ) to show object properties in given HTML
* element .
*
* This has been used for the showing , editing properties of the node .
* This has also been used for creating a node .
* * /
showProperties : function ( item , data , panel , action ) {
var that = this ,
tree = pgAdmin . Browser . tree ,
j = panel . $container . find ( '.obj_properties' ) . first ( ) ,
view = j . data ( 'obj-view' ) ,
2020-01-31 01:07:44 -06:00
content = $ ( '<div></div>' )
2019-10-30 05:13:29 -05:00
. addClass ( 'pg-prop-content col-12' ) ,
confirm _close = true ;
2017-06-12 10:43:29 -05:00
2018-01-12 01:29:51 -06:00
// Handle key press events for Cancel, save and help button
var handleKeyDown = function ( event , context ) {
// If called on panel other than node_props, return
if ( panel && panel [ '_type' ] !== 'node_props' ) return ;
switch ( event . which ) {
case keyCode . ESCAPE :
2019-10-30 05:13:29 -05:00
closePanel ( true ) ;
2018-01-12 01:29:51 -06:00
break ;
case keyCode . ENTER :
2019-03-14 10:11:16 -05:00
// Return if event is fired from child element
2018-01-12 01:29:51 -06:00
if ( event . target !== context ) return ;
if ( view && view . model && view . model . sessChanged ( ) ) {
var btn = $ ( event . target ) . closest ( '.obj_properties' )
2019-03-14 10:11:16 -05:00
. find ( '.pg-prop-btn-group' )
. find ( 'button.btn-primary' ) ;
2018-01-12 01:29:51 -06:00
onSave . call ( this , view , btn ) ;
}
break ;
case keyCode . F1 :
onDialogHelp ( ) ;
break ;
default :
break ;
}
} . bind ( panel ) ;
setTimeout ( function ( ) {
// Register key press events with panel element
panel . $container . find ( '.backform-tab' ) . on ( 'keydown' , function ( event ) {
handleKeyDown ( event , this ) ;
} ) ;
} , 200 ) ; // wait for panel tab to render
// Template function to create the status bar
var createStatusBar = function ( location ) {
2019-12-02 02:45:31 -06:00
var statusBar = $ ( '<div role="status"></div>' ) . addClass (
2018-01-12 01:29:51 -06:00
'pg-prop-status-bar'
) . appendTo ( j ) ;
statusBar . css ( 'visibility' , 'hidden' ) ;
if ( location == 'header' ) {
statusBar . appendTo ( that . header ) ;
} else {
statusBar . prependTo ( that . footer ) ;
}
that . statusBar = statusBar ;
return statusBar ;
2016-04-14 02:20:15 -05:00
} . bind ( panel ) ,
2015-06-30 00:51:55 -05:00
// Template function to create the button-group
2016-01-04 02:04:45 -06:00
createButtons = function ( buttons , location , extraClasses ) {
2018-01-12 01:29:51 -06:00
// Arguments must be non-zero length array of type
2015-06-30 00:51:55 -05:00
// object, which contains following attributes:
// label, type, extraClasses, register
if ( buttons && _ . isArray ( buttons ) && buttons . length > 0 ) {
// All buttons will be created within a single
// div area.
var btnGroup =
2019-05-15 06:07:06 -05:00
$ ( '<div class="pg-prop-btn-group"></div>' ) ,
2015-07-14 02:45:59 -05:00
// Template used for creating a button
tmpl = _ . template ( [
2019-05-15 06:07:06 -05:00
'<button tabindex="0" type="<%= type %>" ' ,
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
'class="btn <%=extraClasses.join(\' \')%>"' ,
2019-12-03 01:17:42 -06:00
'<% if (disabled) { %> disabled="disabled"<% } %> title="<%-tooltip%>"' ,
'<% if (label != "") {} else { %> aria-label="<%-tooltip%>"<% } %> >' ,
2018-01-12 01:29:51 -06:00
'<span class="<%= icon %>"></span><% if (label != "") { %> <%-label%><% } %></button>' ,
] . join ( ' ' ) ) ;
if ( location == 'header' ) {
btnGroup . appendTo ( that . header ) ;
} else {
btnGroup . appendTo ( that . footer ) ;
2016-01-04 02:04:45 -06:00
}
2015-07-14 02:45:59 -05:00
if ( extraClasses ) {
btnGroup . addClass ( extraClasses ) ;
}
2015-06-30 00:51:55 -05:00
_ . each ( buttons , function ( btn ) {
// Create the actual button, and append to
// the group div
// icon may not present for this button
if ( ! btn . icon ) {
2018-01-12 01:29:51 -06:00
btn . icon = '' ;
2015-06-30 00:51:55 -05:00
}
var b = $ ( tmpl ( btn ) ) ;
btnGroup . append ( b ) ;
// Register is a callback to set callback
2015-12-17 04:43:36 -06:00
// for certain operation for this button.
2015-06-30 00:51:55 -05:00
btn . register ( b ) ;
} ) ;
return btnGroup ;
}
return null ;
2016-04-14 02:20:15 -05:00
} . bind ( panel ) ,
2015-06-30 00:51:55 -05:00
// Callback to show object properties
properties = function ( ) {
2015-07-14 02:45:59 -05:00
2016-06-29 06:16:02 -05:00
// Avoid unnecessary reloads
2020-07-14 05:15:01 -05:00
var i = tree . selected ( ) ,
2018-01-12 01:29:51 -06:00
d = i && tree . itemData ( i ) ,
n = i && d && pgBrowser . Nodes [ d . _type ] ,
treeHierarchy = n . getTreeNodeHierarchy ( i ) ;
2016-06-29 06:16:02 -05:00
2020-07-14 05:15:01 -05:00
if ( _ . isEqual ( $ ( this ) . data ( 'node-prop' ) , treeHierarchy ) ) {
2016-06-29 06:16:02 -05:00
return ;
}
// Cache the current IDs for next time
2020-07-14 05:15:01 -05:00
$ ( this ) . data ( 'node-prop' , treeHierarchy ) ;
2016-04-14 02:20:15 -05:00
2015-07-14 02:45:59 -05:00
if ( ! content . hasClass ( 'has-pg-prop-btn-group' ) )
content . addClass ( 'has-pg-prop-btn-group' ) ;
2015-06-30 00:51:55 -05:00
// We need to release any existing view, before
// creating new view.
if ( view ) {
// Release the view
2018-01-12 01:29:51 -06:00
view . remove ( {
data : true ,
internal : true ,
silent : true ,
} ) ;
2017-07-27 06:55:07 -05:00
// Deallocate the view
// delete view;
2015-06-30 00:51:55 -05:00
view = null ;
// Reset the data object
j . data ( 'obj-view' , null ) ;
}
// Make sure the HTML element is empty.
j . empty ( ) ;
2016-01-04 02:04:45 -06:00
that . header = $ ( '<div></div>' ) . addClass (
2018-01-12 01:29:51 -06:00
'pg-prop-header'
) . appendTo ( j ) ;
2016-01-04 02:04:45 -06:00
that . footer = $ ( '<div></div>' ) . addClass (
2018-01-12 01:29:51 -06:00
'pg-prop-footer'
) . appendTo ( j ) ;
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 05:44:55 -06:00
2015-06-30 00:51:55 -05:00
// Create a view to show the properties in fieldsets
2016-09-26 09:10:38 -05:00
view = that . getView ( item , 'properties' , content , data , 'fieldset' , undefined , j ) ;
2015-06-30 00:51:55 -05:00
if ( view ) {
// Save it for release it later
j . data ( 'obj-view' , view ) ;
2016-01-04 02:04:45 -06:00
2015-06-30 00:51:55 -05:00
// Create proper buttons
2016-01-04 02:04:45 -06:00
2015-06-30 00:51:55 -05:00
var buttons = [ ] ;
2016-08-22 06:30:16 -05:00
2015-06-30 00:51:55 -05:00
buttons . push ( {
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 05:44:55 -06:00
label : gettext ( 'Edit' ) ,
2018-01-12 01:29:51 -06:00
type : 'edit' ,
2017-06-07 05:23:02 -05:00
tooltip : gettext ( 'Edit' ) ,
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 05:44:55 -06:00
extraClasses : [ 'btn' , 'btn-primary' , 'pull-right' , 'm-1' ] ,
2020-08-13 01:34:00 -05:00
icon : 'fa fa-sm fa-pencil-alt' ,
2020-10-12 03:19:54 -05:00
disabled : _ . isFunction ( that . canEdit ) ? ! that . canEdit . apply ( that , [ d , i ] ) : ! that . canEdit ,
2015-06-30 00:51:55 -05:00
register : function ( btn ) {
2018-05-25 10:26:37 -05:00
btn . on ( 'click' , ( ) => {
2015-06-30 00:51:55 -05:00
onEdit ( ) ;
} ) ;
2018-01-12 01:29:51 -06:00
} ,
2015-06-30 00:51:55 -05:00
} ) ;
2016-08-22 06:30:16 -05:00
2016-04-12 07:35:47 -05:00
buttons . push ( {
2018-01-12 01:29:51 -06:00
label : '' ,
type : 'help' ,
2017-06-07 05:23:02 -05:00
tooltip : gettext ( 'SQL help for this object type.' ) ,
2020-07-20 01:21:21 -05:00
extraClasses : [ 'btn-primary-icon' , 'btn-primary-icon' , 'm-1' ] ,
2020-08-13 07:18:04 -05:00
icon : 'fa fa-info' ,
2016-04-12 07:35:47 -05:00
disabled : ( that . sqlAlterHelp == '' && that . sqlCreateHelp == '' ) ? true : false ,
register : function ( btn ) {
2018-05-25 10:26:37 -05:00
btn . on ( 'click' , ( ) => {
2016-04-12 07:35:47 -05:00
onSqlHelp ( ) ;
} ) ;
2018-01-12 01:29:51 -06:00
} ,
2016-04-12 07:35:47 -05:00
} ) ;
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 05:44:55 -06:00
createButtons ( buttons , 'header' , 'pg-prop-btn-group-above' ) ;
2015-06-30 00:51:55 -05:00
}
j . append ( content ) ;
2016-04-14 02:20:15 -05:00
} . bind ( panel ) ,
2016-04-12 07:35:47 -05:00
onSqlHelp = function ( ) {
// Construct the URL
2017-07-18 09:13:16 -05:00
var server = that . getTreeNodeHierarchy ( item ) . server ;
2016-04-12 07:35:47 -05:00
2017-07-18 09:13:16 -05:00
var url = pgBrowser . utils . pg _help _path ;
2016-04-12 07:35:47 -05:00
if ( server . server _type == 'ppas' ) {
2017-07-18 09:13:16 -05:00
url = pgBrowser . utils . edbas _help _path ;
2016-04-12 07:35:47 -05:00
}
2019-03-22 09:09:24 -05:00
var fullUrl = '' ;
2016-04-12 07:35:47 -05:00
if ( that . sqlCreateHelp == '' && that . sqlAlterHelp != '' ) {
2020-05-22 03:41:35 -05:00
fullUrl = help . getHelpUrl ( url , that . sqlAlterHelp , server . version , server . server _type ) ;
2016-04-12 07:35:47 -05:00
} else if ( that . sqlCreateHelp != '' && that . sqlAlterHelp == '' ) {
2020-05-22 03:41:35 -05:00
fullUrl = help . getHelpUrl ( url , that . sqlCreateHelp , server . version , server . server _type ) ;
2016-04-12 07:35:47 -05:00
} else {
if ( view . model . isNew ( ) ) {
2020-05-22 03:41:35 -05:00
fullUrl = help . getHelpUrl ( url , that . sqlCreateHelp , server . version , server . server _type ) ;
2016-04-12 07:35:47 -05:00
} else {
2020-05-22 03:41:35 -05:00
fullUrl = help . getHelpUrl ( url , that . sqlAlterHelp , server . version , server . server _type ) ;
2016-04-12 07:35:47 -05:00
}
}
2019-03-22 09:09:24 -05:00
window . open ( fullUrl , 'postgres_help' ) ;
2016-04-14 02:20:15 -05:00
} . bind ( panel ) ,
2016-05-16 10:25:51 -05:00
onDialogHelp = function ( ) {
2019-02-12 10:07:38 -06:00
window . open ( that . dialogHelp , 'pgadmin_help' ) ;
2016-05-16 10:25:51 -05:00
} . bind ( panel ) ,
2019-11-05 02:58:03 -06:00
warnBeforeChangesLost = function ( warn _text , yes _callback ) {
2020-07-14 05:15:01 -05:00
var $props = this . $container . find ( '.obj_properties' ) . first ( ) ,
objview = $props && $props . data ( 'obj-view' ) ,
2019-11-05 02:58:03 -06:00
self = this ;
let confirm _on _properties _close = pgBrowser . get _preferences _for _module ( 'browser' ) . confirm _on _properties _close ;
2020-07-14 05:15:01 -05:00
if ( confirm _on _properties _close && confirm _close && objview && objview . model ) {
if ( objview . model . sessChanged ( ) ) {
2019-11-05 02:58:03 -06:00
Alertify . confirm (
gettext ( 'Warning' ) ,
warn _text ,
function ( ) {
setTimeout ( function ( ) {
yes _callback ( ) ;
} . bind ( self ) , 50 ) ;
return true ;
} ,
function ( ) {
return true ;
}
) . set ( 'labels' , {
ok : gettext ( 'Yes' ) ,
cancel : gettext ( 'No' ) ,
} ) . show ( ) ;
} else {
return true ;
}
} else {
yes _callback ( ) ;
return true ;
}
} . bind ( panel ) ,
2019-12-01 23:55:51 -06:00
warnBeforeAttributeChange = function ( yes _callback ) {
2020-07-14 05:15:01 -05:00
var $props = this . $container . find ( '.obj_properties' ) . first ( ) ,
objview = $props && $props . data ( 'obj-view' ) ,
2019-12-01 23:55:51 -06:00
self = this ;
2020-07-14 05:15:01 -05:00
if ( objview && objview . model && ! _ . isUndefined ( objview . model . warn _text ) && ! _ . isNull ( objview . model . warn _text ) ) {
2019-12-01 23:55:51 -06:00
let warn _text ;
2020-07-14 05:15:01 -05:00
warn _text = gettext ( objview . model . warn _text ) ;
if ( objview . model . sessChanged ( ) ) {
2019-12-01 23:55:51 -06:00
Alertify . confirm (
gettext ( 'Warning' ) ,
warn _text ,
function ( ) {
setTimeout ( function ( ) {
yes _callback ( ) ;
} . bind ( self ) , 50 ) ;
return true ;
} ,
function ( ) {
return true ;
}
) . set ( 'labels' , {
ok : gettext ( 'Yes' ) ,
cancel : gettext ( 'No' ) ,
} ) . show ( ) ;
} else {
return true ;
}
} else {
yes _callback ( ) ;
return true ;
}
} . bind ( panel ) ,
2020-06-30 08:45:23 -05:00
informBeforeAttributeChange = function ( ok _callback ) {
2020-07-14 05:15:01 -05:00
var $props = this . $container . find ( '.obj_properties' ) . first ( ) ,
objview = $props && $props . data ( 'obj-view' ) ;
2020-06-30 08:45:23 -05:00
2020-07-14 05:15:01 -05:00
if ( objview && objview . model && ! _ . isUndefined ( objview . model . inform _text ) && ! _ . isNull ( objview . model . inform _text ) ) {
2020-06-30 08:45:23 -05:00
Alertify . alert (
gettext ( 'Warning' ) ,
2020-07-14 05:15:01 -05:00
gettext ( objview . model . inform _text )
2020-06-30 08:45:23 -05:00
) ;
}
ok _callback ( ) ;
return true ;
} . bind ( panel ) ,
2020-07-14 05:15:01 -05:00
onSave = function ( _view , saveBtn ) {
var m = _view . model ,
2017-06-12 10:43:29 -05:00
d = m . toJSON ( true ) ,
// Generate a timer for the request
2018-01-12 01:29:51 -06:00
timer = setTimeout ( function ( ) {
2017-06-12 10:43:29 -05:00
$ ( '.obj_properties' ) . addClass ( 'show_progress' ) ;
2018-01-12 01:29:51 -06:00
} , 1000 ) ;
2018-01-03 08:49:08 -06:00
// Prevent subsequent save operation by disabling Save button
2018-01-12 01:29:51 -06:00
if ( saveBtn )
2018-01-03 08:49:08 -06:00
$ ( saveBtn ) . prop ( 'disabled' , true ) ;
2017-06-12 10:43:29 -05:00
if ( d && ! _ . isEmpty ( d ) ) {
m . save ( { } , {
attrs : d ,
validate : false ,
cache : false ,
2018-01-03 08:49:08 -06:00
wait : true ,
2017-06-12 10:43:29 -05:00
success : function ( ) {
onSaveFunc . call ( ) ;
// Hide progress cursor
$ ( '.obj_properties' ) . removeClass ( 'show_progress' ) ;
clearTimeout ( timer ) ;
// Removing the node-prop property of panel
// so that we show updated data on panel
var pnlProperties = pgBrowser . docker . findPanels ( 'properties' ) [ 0 ] ,
pnlSql = pgBrowser . docker . findPanels ( 'sql' ) [ 0 ] ,
pnlStats = pgBrowser . docker . findPanels ( 'statistics' ) [ 0 ] ,
pnlDependencies = pgBrowser . docker . findPanels ( 'dependencies' ) [ 0 ] ,
pnlDependents = pgBrowser . docker . findPanels ( 'dependents' ) [ 0 ] ;
2018-01-12 01:29:51 -06:00
if ( pnlProperties )
$ ( pnlProperties ) . removeData ( 'node-prop' ) ;
if ( pnlSql )
$ ( pnlSql ) . removeData ( 'node-prop' ) ;
if ( pnlStats )
$ ( pnlStats ) . removeData ( 'node-prop' ) ;
if ( pnlDependencies )
$ ( pnlDependencies ) . removeData ( 'node-prop' ) ;
if ( pnlDependents )
$ ( pnlDependents ) . removeData ( 'node-prop' ) ;
2017-06-12 10:43:29 -05:00
} ,
2020-07-14 05:15:01 -05:00
error : function ( _m , jqxhr ) {
2017-06-12 10:43:29 -05:00
Alertify . pgNotifier (
2018-01-12 01:29:51 -06:00
'error' , jqxhr ,
gettext ( 'Error saving properties' )
2017-11-20 07:31:03 -06:00
) ;
2017-06-12 10:43:29 -05:00
// Hide progress cursor
$ ( '.obj_properties' ) . removeClass ( 'show_progress' ) ;
clearTimeout ( timer ) ;
2018-01-12 01:29:51 -06:00
if ( saveBtn )
2018-01-03 08:49:08 -06:00
$ ( saveBtn ) . prop ( 'disabled' , false ) ;
2018-01-12 01:29:51 -06:00
} ,
2017-06-12 10:43:29 -05:00
} ) ;
}
} . bind ( panel ) ,
2015-06-30 00:51:55 -05:00
editFunc = function ( ) {
2020-07-14 05:15:01 -05:00
var self = this ;
2015-06-30 00:51:55 -05:00
if ( action && action == 'properties' ) {
action = 'edit' ;
}
2020-07-14 05:15:01 -05:00
self . $container . attr ( 'action-mode' , action ) ;
2015-06-30 00:51:55 -05:00
// We need to release any existing view, before
// creating the new view.
if ( view ) {
// Release the view
2018-01-12 01:29:51 -06:00
view . remove ( {
data : true ,
internal : true ,
silent : true ,
} ) ;
2017-07-27 06:55:07 -05:00
// Deallocate the view
2015-06-30 00:51:55 -05:00
view = null ;
// Reset the data object
j . data ( 'obj-view' , null ) ;
}
// Make sure the HTML element is empty.
j . empty ( ) ;
2016-01-04 02:04:45 -06:00
that . header = $ ( '<div></div>' ) . addClass (
2018-01-12 01:29:51 -06:00
'pg-prop-header'
) . appendTo ( j ) ;
2016-01-04 02:04:45 -06:00
that . footer = $ ( '<div></div>' ) . addClass (
2018-01-12 01:29:51 -06:00
'pg-prop-footer'
) . appendTo ( j ) ;
2016-01-04 02:04:45 -06:00
2016-01-12 10:48:54 -06:00
var updateButtons = function ( hasError , modified ) {
var btnGroup = this . find ( '.pg-prop-btn-group' ) ,
2018-01-12 01:29:51 -06:00
btnSave = btnGroup . find ( 'button.btn-primary' ) ,
2019-01-02 03:35:15 -06:00
btnReset = btnGroup . find ( 'button.btn-secondary[type="reset"]' ) ;
2015-10-28 12:06:09 -05:00
2016-01-12 10:48:54 -06:00
if ( hasError || ! modified ) {
2015-10-28 12:06:09 -05:00
btnSave . prop ( 'disabled' , true ) ;
btnSave . attr ( 'disabled' , 'disabled' ) ;
2016-01-12 10:48:54 -06:00
} else {
btnSave . prop ( 'disabled' , false ) ;
btnSave . removeAttr ( 'disabled' ) ;
}
if ( ! modified ) {
2015-10-28 12:06:09 -05:00
btnReset . prop ( 'disabled' , true ) ;
btnReset . attr ( 'disabled' , 'disabled' ) ;
2016-01-12 10:48:54 -06:00
} else {
btnReset . prop ( 'disabled' , false ) ;
btnReset . removeAttr ( 'disabled' ) ;
2015-10-28 12:06:09 -05:00
}
} ;
// Create a view to edit/create the properties in fieldsets
2016-08-29 01:22:50 -05:00
view = that . getView ( item , action , content , data , 'dialog' , updateButtons , j , onCancelFunc ) ;
2015-06-30 00:51:55 -05:00
if ( view ) {
// Save it to release it later
j . data ( 'obj-view' , view ) ;
2015-07-14 02:45:59 -05:00
2020-07-14 05:15:01 -05:00
self . icon (
2018-01-12 01:29:51 -06:00
_ . isFunction ( that [ 'node_image' ] ) ?
2019-03-14 10:11:16 -05:00
( that [ 'node_image' ] ) . apply ( that , [ data , view . model ] ) :
( that [ 'node_image' ] || ( 'icon-' + that . type ) )
2018-01-12 01:29:51 -06:00
) ;
2016-01-17 10:51:02 -06:00
2015-06-30 00:51:55 -05:00
// Create proper buttons
2019-01-22 04:58:32 -06:00
let btn _grp = createButtons ( [ {
2018-01-12 01:29:51 -06:00
label : '' ,
type : 'help' ,
2017-06-07 05:23:02 -05:00
tooltip : gettext ( 'SQL help for this object type.' ) ,
2020-07-20 01:21:21 -05:00
extraClasses : [ 'btn-primary-icon' , 'pull-left' , 'mx-1' ] ,
2020-08-13 07:18:04 -05:00
icon : 'fa fa-info' ,
2016-05-16 10:25:51 -05:00
disabled : ( that . sqlAlterHelp == '' && that . sqlCreateHelp == '' ) ? true : false ,
2016-04-12 07:35:47 -05:00
register : function ( btn ) {
2018-05-25 10:26:37 -05:00
btn . on ( 'click' , ( ) => {
2016-04-12 07:35:47 -05:00
onSqlHelp ( ) ;
} ) ;
2018-01-12 01:29:51 -06:00
} ,
} , {
label : '' ,
type : 'help' ,
2017-06-07 05:23:02 -05:00
tooltip : gettext ( 'Help for this dialog.' ) ,
2020-07-20 01:21:21 -05:00
extraClasses : [ 'btn-primary-icon' , 'pull-left' , 'mx-1' ] ,
2020-08-13 07:18:04 -05:00
icon : 'fa fa-question' ,
2016-05-16 10:25:51 -05:00
disabled : ( that . dialogHelp == '' ) ? true : false ,
register : function ( btn ) {
2018-05-25 10:26:37 -05:00
btn . on ( 'click' , ( ) => {
2016-05-16 10:25:51 -05:00
onDialogHelp ( ) ;
} ) ;
2018-01-12 01:29:51 -06:00
} ,
} , {
label : gettext ( 'Cancel' ) ,
type : 'cancel' ,
2017-06-07 05:23:02 -05:00
tooltip : gettext ( 'Cancel changes to this object.' ) ,
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 05:44:55 -06:00
extraClasses : [ 'btn-secondary' , 'mx-1' ] ,
2020-08-13 01:34:00 -05:00
icon : 'fa fa-times pg-alertify-button' ,
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
disabled : false ,
2015-06-30 00:51:55 -05:00
register : function ( btn ) {
2018-05-25 10:26:37 -05:00
btn . on ( 'click' , ( ) => {
2015-06-30 00:51:55 -05:00
// Removing the action-mode
2020-07-14 05:15:01 -05:00
self . $container . removeAttr ( 'action-mode' ) ;
2019-10-30 05:13:29 -05:00
onCancelFunc . call ( true ) ;
2015-06-30 00:51:55 -05:00
} ) ;
2018-01-12 01:29:51 -06:00
} ,
} , {
label : gettext ( 'Reset' ) ,
type : 'reset' ,
2017-06-07 05:23:02 -05:00
tooltip : gettext ( 'Reset the fields on this dialog.' ) ,
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 05:44:55 -06:00
extraClasses : [ 'btn-secondary' , 'mx-1' ] ,
icon : 'fa fa-recycle pg-alertify-button' ,
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
disabled : true ,
2015-06-30 00:51:55 -05:00
register : function ( btn ) {
2018-05-25 10:26:37 -05:00
btn . on ( 'click' , ( ) => {
2019-11-05 02:58:03 -06:00
warnBeforeChangesLost . call (
2020-07-14 05:15:01 -05:00
self ,
2019-11-05 02:58:03 -06:00
gettext ( 'Changes will be lost. Are you sure you want to reset?' ) ,
function ( ) {
setTimeout ( function ( ) {
editFunc . call ( ) ;
} , 0 ) ;
}
) ;
2015-06-30 00:51:55 -05:00
} ) ;
2018-01-12 01:29:51 -06:00
} ,
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 05:44:55 -06:00
} , {
label : gettext ( 'Save' ) ,
type : 'save' ,
tooltip : gettext ( 'Save this object.' ) ,
extraClasses : [ 'btn-primary' , 'mx-1' ] ,
icon : 'fa fa-save pg-alertify-button' ,
disabled : true ,
register : function ( btn ) {
// Save the changes
btn . on ( 'click' , ( ) => {
2019-12-01 23:55:51 -06:00
warnBeforeAttributeChange . call (
2020-07-14 05:15:01 -05:00
self ,
2019-12-01 23:55:51 -06:00
function ( ) {
2020-07-14 05:15:01 -05:00
informBeforeAttributeChange . call ( self , function ( ) {
2020-06-30 08:45:23 -05:00
setTimeout ( function ( ) {
onSave . call ( this , view , btn ) ;
} , 0 ) ;
} ) ;
2019-12-01 23:55:51 -06:00
}
) ;
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 05:44:55 -06:00
} ) ;
} ,
} ] , 'footer' , 'pg-prop-btn-group-below' ) ;
2019-01-22 04:58:32 -06:00
btn _grp . on ( 'keydown' , 'button' , function ( event ) {
2020-02-04 00:00:33 -06:00
if ( ! event . shiftKey && event . keyCode == 9 && $ ( this ) . nextAll ( 'button:not([disabled])' ) . length == 0 ) {
// set focus back to first focusable element on dialog
view . $el . closest ( '.wcFloating' ) . find ( '[tabindex]:not([tabindex="-1"]' ) . first ( ) . focus ( ) ;
2019-01-22 04:58:32 -06:00
return false ;
}
2020-07-14 05:15:01 -05:00
let btnGroup = $ ( self . $container . find ( '.pg-prop-btn-group' ) ) ;
2020-04-06 02:43:21 -05:00
let el = $ ( btnGroup ) . find ( 'button:first' ) ;
2020-07-14 05:15:01 -05:00
if ( self . $container . find ( '.number-cell.editable:last' ) . is ( ':visible' ) ) {
2020-04-06 02:43:21 -05:00
if ( event . keyCode === 9 && event . shiftKey ) {
if ( $ ( el ) . is ( $ ( event . target ) ) ) {
2020-07-14 05:15:01 -05:00
$ ( self . $container . find ( 'td.editable:last' ) . trigger ( 'click' ) ) ;
2020-04-06 02:43:21 -05:00
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
}
}
}
2019-01-22 04:58:32 -06:00
} ) ;
setTimeout ( function ( ) {
2020-07-14 05:15:01 -05:00
pgBrowser . keyboardNavigation . getDialogTabNavigator ( self . pgElContainer ) ;
2019-01-22 04:58:32 -06:00
} , 200 ) ;
2018-01-12 01:29:51 -06:00
}
2015-07-14 02:45:59 -05:00
2016-01-04 02:04:45 -06:00
// Create status bar.
createStatusBar ( 'footer' ) ;
2015-07-14 02:45:59 -05:00
// Add some space, so that - button group does not override the
// space
content . addClass ( 'pg-prop-has-btn-group-below' ) ;
// Show contents before buttons
j . prepend ( content ) ;
2020-12-24 01:56:25 -06:00
// add required attributes to select2 input to resolve accessibility issue.
$ ( '.select2-search__field' ) . attr ( 'aria-label' , 'select2' ) ;
2020-02-04 00:00:33 -06:00
view . $el . closest ( '.wcFloating' ) . find ( '.wcFrameButtonBar > .wcFrameButton[style!="display: none;"]' ) . on ( 'keydown' , function ( e ) {
if ( e . shiftKey && e . keyCode === 9 ) {
e . stopPropagation ( ) ;
setTimeout ( ( ) => {
view . $el . closest ( '.wcFloating' ) . find ( '[tabindex]:not([tabindex="-1"]):not([disabled])' ) . last ( ) . focus ( ) ;
} , 10 ) ;
}
} ) ;
2016-04-14 02:20:15 -05:00
} . bind ( panel ) ,
2019-10-30 05:13:29 -05:00
closePanel = function ( confirm _close _flag ) {
if ( ! _ . isUndefined ( confirm _close _flag ) ) {
confirm _close = confirm _close _flag ;
}
2015-06-30 00:51:55 -05:00
// Closing this panel
2016-04-15 08:02:21 -05:00
this . close ( ) ;
} . bind ( panel ) ,
2020-07-14 05:15:01 -05:00
updateTreeItem = function ( obj ) {
2016-08-29 09:36:48 -05:00
var _old = data ,
2018-01-12 01:29:51 -06:00
_new = _ . clone ( view . model . tnode ) ,
info = _ . clone ( view . model . node _info ) ;
2016-04-14 02:20:15 -05:00
2016-04-29 05:11:24 -05:00
// Clear the cache for this node now.
2018-01-12 01:29:51 -06:00
setTimeout ( function ( ) {
2020-07-14 05:15:01 -05:00
obj . clear _cache . apply ( obj , item ) ;
2018-01-12 01:29:51 -06:00
} , 0 ) ;
2016-04-29 05:11:24 -05:00
pgBrowser . Events . trigger (
2016-08-29 09:36:48 -05:00
'pgadmin:browser:tree:update' ,
_old , _new , info , {
2017-07-07 01:25:55 -05:00
success : function ( _item , _newNodeData , _oldNodeData ) {
pgBrowser . Events . trigger (
'pgadmin:browser:node:updated' , _item , _newNodeData ,
_oldNodeData
) ;
2016-08-29 09:36:48 -05:00
pgBrowser . Events . trigger (
2017-07-07 01:25:55 -05:00
'pgadmin:browser:node:' + _newNodeData . _type + ':updated' ,
_item , _newNodeData , _oldNodeData
2016-08-29 09:36:48 -05:00
) ;
2018-01-12 01:29:51 -06:00
} ,
2016-08-29 09:36:48 -05:00
}
2016-04-29 05:11:24 -05:00
) ;
2019-10-30 05:13:29 -05:00
closePanel ( false ) ;
2015-06-30 00:51:55 -05:00
} ,
2020-07-14 05:15:01 -05:00
saveNewNode = function ( obj ) {
var $props = this . $container . find ( '.obj_properties' ) . first ( ) ,
objview = $props . data ( 'obj-view' ) ;
2016-04-29 05:11:24 -05:00
// Clear the cache for this node now.
2018-01-12 01:29:51 -06:00
setTimeout ( function ( ) {
2020-07-14 05:15:01 -05:00
obj . clear _cache . apply ( obj , item ) ;
2018-01-12 01:29:51 -06:00
} , 0 ) ;
2016-08-29 09:36:48 -05:00
try {
pgBrowser . Events . trigger (
2020-07-14 05:15:01 -05:00
'pgadmin:browser:tree:add' , _ . clone ( objview . model . tnode ) ,
_ . clone ( objview . model . node _info )
2016-08-29 09:36:48 -05:00
) ;
} catch ( e ) {
2018-01-12 01:29:51 -06:00
console . warn ( e . stack || e ) ;
2015-06-30 00:51:55 -05:00
}
2019-10-30 05:13:29 -05:00
closePanel ( false ) ;
2016-04-29 05:11:24 -05:00
} . bind ( panel , that ) ,
2015-06-30 00:51:55 -05:00
editInNewPanel = function ( ) {
// Open edit in separate panel
setTimeout ( function ( ) {
that . callbacks . show _obj _properties . apply ( that , [ {
'action' : 'edit' ,
2018-01-12 01:29:51 -06:00
'item' : item ,
2015-06-30 00:51:55 -05:00
} ] ) ;
} , 0 ) ;
} ,
2016-04-15 08:02:21 -05:00
onCancelFunc = closePanel ,
2016-04-29 05:11:24 -05:00
onSaveFunc = updateTreeItem . bind ( panel , that ) ,
2016-04-14 02:20:15 -05:00
onEdit = editFunc . bind ( panel ) ;
2015-06-30 00:51:55 -05:00
if ( action ) {
2018-01-12 01:29:51 -06:00
if ( action == 'create' ) {
2016-04-15 08:02:21 -05:00
onSaveFunc = saveNewNode ;
2015-06-30 00:51:55 -05:00
}
if ( action != 'properties' ) {
// We need to keep track edit/create mode for this panel.
editFunc ( ) ;
} else {
properties ( ) ;
}
} else {
/* Show properties */
properties ( ) ;
2016-04-14 02:20:15 -05:00
onEdit = editInNewPanel . bind ( panel ) ;
2015-06-30 00:51:55 -05:00
}
2016-04-15 05:44:01 -05:00
if ( panel . closeable ( ) ) {
2019-11-05 02:58:03 -06:00
panel . on ( wcDocker . EVENT . CLOSING , warnBeforeChangesLost . bind (
panel ,
gettext ( 'Changes will be lost. Are you sure you want to close the dialog?' ) ,
function ( ) {
panel . off ( wcDocker . EVENT . CLOSING ) ;
panel . close ( ) ;
2019-10-29 09:31:43 -05:00
}
2019-11-05 02:58:03 -06:00
) ) ;
2019-10-29 09:31:43 -05:00
2016-04-15 05:44:01 -05:00
var onCloseFunc = function ( ) {
2020-07-14 05:15:01 -05:00
var $props = this . $container . find ( '.obj_properties' ) . first ( ) ,
objview = $props && $props . data ( 'obj-view' ) ;
2016-04-15 05:44:01 -05:00
2020-07-14 05:15:01 -05:00
if ( objview ) {
objview . remove ( {
2018-01-12 01:29:51 -06:00
data : true ,
internal : true ,
silent : true ,
} ) ;
2016-04-15 05:44:01 -05:00
}
} . bind ( panel ) ;
panel . on ( wcDocker . EVENT . CLOSED , onCloseFunc ) ;
}
2015-06-30 00:51:55 -05:00
} ,
2016-06-02 19:27:48 -05:00
_find _parent _node : function ( t , i , d ) {
if ( this . parent _type ) {
d = d || t . itemData ( i ) ;
if ( _ . isString ( this . parent _type ) ) {
if ( this . parent _type == d . _type ) {
return i ;
}
2018-01-12 01:29:51 -06:00
while ( t . hasParent ( i ) ) {
2016-06-02 19:27:48 -05:00
i = t . parent ( i ) ;
d = t . itemData ( i ) ;
if ( this . parent _type == d . _type )
return i ;
}
} else {
if ( _ . indexOf ( this . parent _type , d . _type ) >= 0 ) {
return i ;
}
2018-01-12 01:29:51 -06:00
while ( t . hasParent ( i ) ) {
2016-06-02 19:27:48 -05:00
i = t . parent ( i ) ;
d = t . itemData ( i ) ;
if ( _ . indexOf ( this . parent _type , d . _type ) >= 0 )
return i ;
}
}
}
return null ;
} ,
2015-06-30 00:51:55 -05:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Generate the URL for different operations
*
* arguments :
* type : Create / drop / edit / properties / sql / depends / statistics
* d : Provide the ItemData for the current item node
* with _id : Required id information at the end ?
2020-01-01 01:29:48 -06:00
* jump _after _node : This will skip all the value between jump _after _node
* to the last node , excluding jump _after _node and the last node . This is particularly
* helpful in partition table where we need to skip parent table OID of a partitioned
* table in URL formation . Partitioned table itself is a "table" and can be multilevel
2015-06-30 00:51:55 -05:00
* Supports url generation for create , drop , edit , properties , sql ,
* depends , statistics
* /
2020-01-01 01:29:48 -06:00
generate _url : function ( item , type , d , with _id , info , jump _after _node ) {
2017-08-17 11:13:07 -05:00
var opURL = {
2018-01-12 01:29:51 -06:00
'create' : 'obj' ,
'drop' : 'obj' ,
'edit' : 'obj' ,
'properties' : 'obj' ,
'statistics' : 'stats' ,
} ,
self = this ,
2016-01-09 08:24:50 -06:00
priority = - Infinity ;
2017-08-17 11:13:07 -05:00
var treeInfo = ( _ . isUndefined ( item ) || _ . isNull ( item ) ) ?
2016-01-09 08:24:50 -06:00
info || { } : this . getTreeNodeHierarchy ( item ) ;
2017-08-17 11:13:07 -05:00
var actionType = type in opURL ? opURL [ type ] : type ;
var itemID = with _id && d . _type == self . type ? encodeURIComponent ( d . _id ) : '' ;
2016-01-09 08:24:50 -06:00
if ( self . parent _type ) {
if ( _ . isString ( self . parent _type ) ) {
2020-07-14 05:15:01 -05:00
let p = treeInfo [ self . parent _type ] ;
2016-01-09 08:24:50 -06:00
if ( p ) {
priority = p . priority ;
}
} else {
_ . each ( self . parent _type , function ( o ) {
2020-07-14 05:15:01 -05:00
let p = treeInfo [ o ] ;
2016-01-09 08:24:50 -06:00
if ( p ) {
if ( priority < p . priority ) {
priority = p . priority ;
}
}
} ) ;
}
}
2020-01-01 01:29:48 -06:00
let jump _after _priority = priority ;
if ( jump _after _node && treeInfo [ jump _after _node ] ) {
jump _after _priority = treeInfo [ jump _after _node ] . priority ;
}
2018-01-12 01:29:51 -06:00
var nodePickFunction = function ( treeInfoValue ) {
2020-01-01 01:29:48 -06:00
return ( treeInfoValue . priority <= jump _after _priority || treeInfoValue . priority == priority ) ;
2015-11-19 11:45:48 -06:00
} ;
2020-01-01 01:29:48 -06:00
2017-08-17 11:13:07 -05:00
return generateUrl . generate _url ( pgBrowser . URL , treeInfo , actionType , self . type , nodePickFunction , itemID ) ;
2015-06-30 00:51:55 -05:00
} ,
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
// Base class for Node Data Collection
2016-01-18 22:45:04 -06:00
Collection : pgBrowser . DataCollection ,
Data management within the properties dialog in Create/Edit mode is not
done using the Backbone event management.
Emitting 'pgadmin-session:*' for different operations, when we
create/modify the data within the properties dialog.
We will keep track of each child node using the handler object within
the Model, and Collection objects, also - provides them the name of the
attribute, it represents. It will be used to identify the invalid nested
objects within the existing object.
Also, provide the array of modified variables, which were modified in
the validation function to avoid checking each, and every thing in the
validation function. We will need to validate that particular and the
dependent attributes only.
Also - avoid multiple validation operations from the parent object to
improve performance. And, depends on the event based operations for
validation, instead of integrate the data operation and view operation
within one operation. We do maintain the invalid objects, and validate
them only from the collection objects, which also helps improve the
performance during validation.
2016-01-15 04:29:20 -06:00
// Base class for Node Data Model
2016-01-18 22:45:04 -06:00
Model : pgBrowser . DataModel ,
2018-06-05 05:36:19 -05:00
getTreeNodeHierarchy : pgadminTreeNode . getTreeNodeHierarchyFromIdentifier . bind ( pgBrowser ) ,
2016-01-05 03:06:30 -06:00
cache : function ( url , node _info , level , data ) {
var cached = this . cached = this . cached || { } ,
2018-01-12 01:29:51 -06:00
hash = url ,
min _priority = (
node _info && node _info [ level ] && node _info [ level ] . priority
) || 0 ;
2016-01-05 03:06:30 -06:00
if ( node _info ) {
2018-01-12 01:29:51 -06:00
_ . each ( _ . sortBy ( _ . values ( _ . pick (
node _info ,
function ( v ) {
return ( v . priority <= min _priority ) ;
}
) ) , function ( o ) {
return o . priority ;
} ) , function ( o ) {
2019-10-10 01:35:28 -05:00
hash = commonUtils . sprintf ( '%s/%s' , hash , encodeURI ( o . _id ) ) ;
2018-01-12 01:29:51 -06:00
} ) ;
2016-01-05 03:06:30 -06:00
}
2015-12-26 02:52:19 -06:00
if ( _ . isUndefined ( data ) ) {
2016-04-29 05:11:24 -05:00
var res = cached [ hash ] ;
if ( ! _ . isUndefined ( res ) &&
2018-01-12 01:29:51 -06:00
( res . at - Date . now ( ) > 300000 ) ) {
2016-04-29 05:11:24 -05:00
res = undefined ;
}
return res ;
2015-12-26 02:52:19 -06:00
}
2018-01-12 01:29:51 -06:00
res = cached [ hash ] = {
data : data ,
at : Date . now ( ) ,
level : level ,
} ;
2016-01-05 03:06:30 -06:00
return res ;
2016-04-29 05:11:24 -05:00
} ,
clear _cache : function ( item ) {
/ *
* Reset the cache , when new node is created .
*
* FIXME :
* At the moment , we will clear all the cache for this node . But - we
* would like to clear the cache only this nodes parent , so that - it
* fetches the new data .
* /
this . cached = { } ;
2017-12-13 09:17:17 -06:00
// Trigger Notify event about node's cache
var self = this ;
pgBrowser . Events . trigger (
'pgadmin:browser:node:' + self . type + ':cache_cleared' ,
item , self
) ;
2016-04-29 05:11:24 -05:00
} ,
cache _level : function ( node _info , with _id ) {
if ( node _info ) {
if ( with _id && this . type in node _info ) {
return this . type ;
}
if ( _ . isArray ( this . parent _type ) ) {
for ( var parent in this . parent _type ) {
if ( parent in node _info ) {
return parent ;
}
}
return this . type ;
}
return this . parent _type ;
}
2018-01-12 01:29:51 -06:00
} ,
2015-06-30 00:51:55 -05:00
} ) ;
return pgAdmin . Browser . Node ;
} ) ;