diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index 56b9c3c20..3ae4fc036 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -8,7 +8,8 @@
##########################################################################
import json
from abc import ABCMeta, abstractmethod, abstractproperty
-from flask import render_template, request, make_response, jsonify, current_app
+from flask import render_template, request, make_response, jsonify, \
+ current_app, url_for
from flask.ext.security import login_required, current_user
from pgadmin.settings.settings_model import db, Server, ServerGroup, User
from pgadmin.utils.menu import MenuItem
@@ -84,6 +85,25 @@ class ServerModule(sg.ServerGroupPluginModule):
return snippets
+ def get_own_javascripts(self):
+ scripts = []
+
+ scripts.extend([{
+ 'name': 'pgadmin.node.server',
+ 'path': url_for('browser.index') + '%s/module' % self.node_type,
+ 'when': self.script_load
+ },
+ {
+ 'name': 'pgadmin.browser.server.privilege',
+ 'path': url_for('browser.index') + 'server/static/js/privilege',
+ 'when': self.node_type
+ }])
+
+ for module in self.submodules:
+ scripts.extend(module.get_own_javascripts())
+
+ return scripts
+
class ServerMenuItem(MenuItem):
def __init__(self, **kwargs):
@@ -606,7 +626,8 @@ class ServerNode(PGChildNodeView):
),
'connected': True,
'type': manager.server_type,
- 'version': manager.version
+ 'version': manager.version,
+ 'db': manager.db
}
)
diff --git a/web/pgadmin/browser/server_groups/servers/static/js/privilege.js b/web/pgadmin/browser/server_groups/servers/static/js/privilege.js
new file mode 100644
index 000000000..190183e2f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/static/js/privilege.js
@@ -0,0 +1,431 @@
+(function(root, factory) {
+ // Set up Backform appropriately for the environment. Start with AMD.
+ if (typeof define === 'function' && define.amd) {
+ define(['underscore', 'jquery', 'backbone', 'backform', 'backgrid', 'alertify', 'pgadmin.browser.node'],
+ function(_, $, Backbone, Backform, Backgrid, Alertify, pgNode) {
+ // Export global even in AMD case in case this script is loaded with
+ // others that may still expect a global Backform.
+ return factory(root, _, $, Backbone, Backform, Alertify, pgNode);
+ });
+
+ // Next for Node.js or CommonJS. jQuery may not be needed as a module.
+ } else if (typeof exports !== 'undefined') {
+ var _ = require('underscore') || root._,
+ $ = root.jQuery || root.$ || root.Zepto || root.ender,
+ Backbone = require('backbone') || root.Backbone,
+ Backform = require('backform') || root.Backform;
+ Alertify = require('alertify') || root.Alertify;
+ pgAdmin = require('pgadmin.browser.node') || root.pgAdmin.Browser.Node;
+ factory(root, _, $, Backbone, Backform, Alertify, pgNode);
+
+ // Finally, as a browser global.
+ } else {
+ factory(root, root._, (root.jQuery || root.Zepto || root.ender || root.$), root.Backbone, root.Backform, root.pgAdmin.Browser.Node);
+ }
+} (this, function(root, _, $, Backbone, Backform, Alertify, pgNode) {
+
+ /**
+ * Each Privilege, supporeted by an database object, will be represented
+ * using this Model.
+ *
+ * Defaults:
+ * privilege_type -> Name of the permission
+ * i.e. CREATE, TEMPORARY, CONNECT, etc.
+ * privilege -> Has privilege? (true/false)
+ * with_grant -> Has privilege with grant option (true/false)
+ **/
+ var PrivilegeModel = pgNode.Model.extend({
+ idAttribute: 'privilege_type',
+ defaults: {
+ privilege_type: undefined,
+ privilege: false,
+ with_grant: false
+ }
+ });
+
+ /**
+ * A database object has privileges item list (aclitem[]).
+ *
+ * This model represents the individual privilege item (aclitem).
+ * It has basically three properties:
+ * + grantee - Role to which that privilege applies to.
+ * Empty value represents to PUBLIC.
+ * + grantor - Granter who has given this permission.
+ * + privileges - Privileges for that role.
+ **/
+ var PrivilegeRoleModel = pgNode.PrivilegeRoleModel = pgNode.Model.extend({
+ defaults: {
+ grantee: undefined,
+ grantor: undefined,
+ privileges: undefined
+ },
+ /*
+ * Each of the database object needs to extend this model, which should
+ * provide the type of privileges (it supports).
+ */
+ privileges:[],
+
+ schema: [{
+ id: 'grantee', label:'Grantee', type:'text', group: null, cell: 'string',
+ disabled: true, cellHeaderClasses: 'width_percent_40'
+ }, {
+ id: 'privileges', label:'Privileges',
+ type: 'collection', model: PrivilegeModel, group: null,
+ disabled: false, cell: 'privilege', control: 'text',
+ cellHeaderClasses: 'width_percent_40'
+ },{
+ id: 'grantor', label: 'Granter', type: 'text', disabled: true
+ }],
+
+ /*
+ * Initialize the model, which will transform the privileges string to
+ * collection of Privilege Model.
+ */
+ initialize: function(attrs, opts) {
+
+ pgNode.Model.prototype.initialize.apply(this, arguments);
+
+ /*
+ * Define the collection of the privilege supported by this model
+ */
+ var privileges = this.get('privileges') || {};
+ if (_.isArray(privileges)) {
+ privileges = new (pgNode.Collection)(
+ models, {
+ model: PrivilegeModel,
+ handler: this.handler || this,
+ silent: true,
+ parse: false
+ });
+ this.set('privileges', privileges, {silent: true});
+ }
+
+ var privs = {};
+ _.each(this.privileges, function(p) {
+ privs[p] = {
+ 'privilige_type': p, 'privilege': false, 'with_grant': false
+ }
+ });
+
+ privileges.each(function(m) {
+ delete privs[m.get('privilege_type')];
+ });
+
+ _.each(privs, function(p) {
+ privileges.add(p, {silent: true});
+ });
+
+ return this;
+ },
+
+ toJSON: function(session) {
+ if (session) {
+ return pgNode.Model.prototype.apply(this, [true, false]);
+ }
+
+ var privileges = [];
+
+ this.attributes['privileges'].each(
+ function(p) {
+ if (p.get('privilege')) {
+ privileges.push(p.toJSON());
+ }
+ });
+
+ return {
+ 'grantee': this.get('grantee'),
+ 'grantor': this.get('grantor'),
+ 'privileges': privileges
+ };
+ }
+ });
+
+ /**
+ Custom cell editor for editing privileges.
+ */
+ var PrivilegeCellEditor = Backgrid.Extension.PrivilegeCellEditor =
+ Backgrid.CellEditor.extend({
+ tagName: "div",
+
+ // All available privileges in the PostgreSQL database server for
+ // generating the label for the specific Control
+ Labels: {
+ "C": "CREATE",
+ "T": "TEMP",
+ "c": "CONNECT",
+ "a": "INSERT",
+ "r": "SELECT",
+ "w": "UPDATE",
+ "d": "DELETE",
+ "D": "TRUNCATE",
+ "x": "REFERENCES",
+ "t": "TRIGGER",
+ "U": "USAGE",
+ "X": "EXECUTE"
+ },
+
+ template: _.template([
+ '
'].join(" "), null, {variable: null}),
+
+ events: {
+ 'change': 'privilegeChanged',
+ 'blur': 'lostFocus'
+ },
+
+ render: function () {
+ this.$el.empty();
+ this.$el.attr('tabindex', '1');
+ this.$el.attr('target', this.elId);
+
+ var collection = this.model.get(this.column.get("name")),
+ tbl = $("").appendTo(this.$el),
+ self = this,
+ privilege = true, with_grant = true;
+
+ // For each privilege generate html template.
+ // List down all the Privilege model.
+ collection.each(function(m) {
+ var d = m.toJSON();
+
+ _.extend(
+ d, {
+ 'target': self.cid,
+ 'header': false
+ });
+ privilege = (privilege && d.privilege);
+ with_grant = (with_grant && privilege && d.with_grant);
+ tbl.append(self.template(d));
+ });
+
+ // Preprend the ALL controls on that table
+ tbl.prepend(
+ self.template({
+ 'target': self.cid,
+ 'name': 'ALL',
+ 'privilege_type': 'ALL',
+ 'privilege': privilege,
+ 'with_grant': with_grant,
+ 'header': true
+ }));
+
+ self.$el.find('input[type=checkbox]').first().focus();
+ self.delegateEvents();
+
+ return this;
+ },
+
+ /*
+ * Listen to the checkbox value change and update the model accordingly.
+ */
+ privilegeChanged: function(ev) {
+ if (ev && ev.target) {
+ /*
+ * We're looking for checkboxes only.
+ */
+ var $el = $(ev.target),
+ privilege_type = $el.attr('privilege'),
+ type = $el.attr('name'),
+ checked = $el.prop('checked'),
+ $tr = $el.closest('tr'),
+ $tbl = $tr.closest('table'),
+ collection = this.model.get('privileges');;
+
+ /*
+ * If the checkbox selected/deselected is for 'ALL', we will select all
+ * the checkbox for each privilege.
+ */
+ if (privilege_type == 'ALL') {
+ var $elGrant = $tr.find('input[name=with_grant]'),
+ $allPrivileges = $tbl.find(
+ 'input[name=privilege][privilege!=\'ALL\']'
+ ),
+ $allGrants = $tbl.find(
+ 'input[name=with_grant][privilege!=\'ALL\']'
+ ),
+ allPrivilege, allWithGrant;
+
+ if (type == 'privilege') {
+ /*
+ * We clicked the privilege checkbox, and not checkbox for with
+ * grant options.
+ */
+ allPrivilege = checked;
+ allWithGrant = false;
+
+ if (checked) {
+ $allPrivileges.prop('checked', true);
+ /*
+ * We have clicked the ALL checkbox, we should be able to select
+ * the grant options too.
+ */
+ $allGrants.prop('disabled', false);
+ $elGrant.prop('disabled', false);
+ } else {
+ /*
+ * ALL checkbox has been deselected, hence - we need to make
+ * sure.
+ * 1. Deselect all the privileges checkboxes
+ * 2. Deselect and disable all with grant privilege checkboxes.
+ * 3. Deselect and disable the checkbox for ALL with grant privilege.
+ */
+ $allPrivileges.prop('checked', false);
+ $elGrant.prop('checked', false),
+ $allGrants.prop('checked', false);
+ $elGrant.prop('disabled', true);
+ $allGrants.prop('disabled', true);
+ }
+ } else {
+ /*
+ * We were able to click the ALL with grant privilege checkbox,
+ * that means, privilege for Privileges are true.
+ *
+ * We need to select/deselect all the with grant options
+ * checkboxes, based on the current value of the ALL with grant
+ * privilege checkbox.
+ */
+ allPrivilege = true;
+ allWithGrant = checked;
+ $allGrants.prop('checked', checked);
+ }
+
+ /*
+ * Set the values for each Privilege Model.
+ */
+ collection.each(function(m) {
+ m.set({'privilege': allPrivilege, 'with_grant': allWithGrant});
+ });
+ } else {
+ /*
+ * Particular privilege has been selected/deselected, which can be
+ * identified using the privilege="X" attribute.
+ */
+ var attrs = {},
+ $tbl = $tr.closest('table'),
+ $allPrivilege = $tbl.find(
+ 'input[name=privilege][privilege=\'ALL\']'
+ ),
+ $allGrant = $tbl.find(
+ 'input[name=with_grant][privilege=\'ALL\']'
+ );
+
+ attrs[type] = checked;
+
+ if (type == 'privilege') {
+ var $elGrant = ($el.closest('tr')).find('input[name=with_grant]');
+ if (!checked) {
+ attrs['with_grant'] = false;
+
+ $elGrant.prop('checked', false).prop('disabled', true);
+ $allPrivilege.prop('checked', false);
+ $allGrant.prop('disabled', true);
+ $allGrant.prop('checked', false);
+ } else {
+ $elGrant.prop('disabled', false);
+ }
+ } else if (!checked) {
+ $allGrant.prop('checked', false);
+ }
+ collection.get(privilege_type).set(attrs);
+
+ if (checked) {
+ var $allPrivileges = $tbl.find(
+ 'input[name=privilege][privilege!=\'ALL\']:checked'
+ );
+
+ if ($allPrivileges.length == collection.models.length) {
+
+ $allPrivilege.prop('checked', true);
+
+ if (type == 'with_grant') {
+ var $allGrants = $tbl.find(
+ 'input[name=with_grant][privilege!=\'ALL\']:checked'
+ );
+ if ($allGrants.length == collection.models.length) {
+ $allGrant.prop('disabled', false);
+ $allGrant.prop('checked', true);
+ }
+ } else {
+ $allGrant.prop('disabled', false);
+ }
+ }
+ }
+ }
+ }
+ },
+
+ lostFocus: function(ev) {
+ /*
+ * We lost the focuse, it's time for us to exit the editor.
+ */
+ var m = this.model;
+ m.trigger('backgrid:edited', m, this.column, new Backgrid.Command(ev));
+ }
+ });
+
+ /*
+ * This will help us transform the privilieges value in proper format to be
+ * displayed in the cell.
+ */
+ var PrivilegeCellFormatter = Backgrid.Extension.PrivilegeCellFormatter =
+ function () {};
+ _.extend(PrivilegeCellFormatter.prototype, {
+ notation: {
+ "CREATE" : "C",
+ "TEMPORARY" : "T",
+ "CONNECT" : "c",
+ "INSERT" : "a",
+ "SELECT" : "r",
+ "UPDATE" : "w",
+ "DELETE" : "d",
+ "TRUNCATE" : "D",
+ "REFERENCES" : "x",
+ "TRIGGER" : "t",
+ "USAGE" : "U",
+ "EXECUTE" : "X"
+ },
+ /**
+ * Takes a raw value from a model and returns an optionally formatted
+ * string for display.
+ */
+ fromRaw: function (rawData, model) {
+ var res = '',
+ self = this;
+
+ if (rawData instanceof Backbone.Collection) {
+ rawData.each(function(m) {
+ if (m.get('privilege')) {
+ res += self.notation[m.get('privilege_type')];
+ if (m.get('with_grant')) {
+ res += '*';
+ }
+ }
+ });
+ }
+ return res;
+ }
+ });
+
+ /*
+ * PrivilegeCell for rendering and taking input for the privileges.
+ */
+ var PrivilegeCell = Backgrid.Extension.PrivilegeCell = Backgrid.Cell.extend({
+ className: "edit-cell",
+ formatter: PrivilegeCellFormatter,
+ editor: PrivilegeCellEditor
+ });
+
+ return PrivilegeRoleModel;
+}));
diff --git a/web/pgadmin/browser/templates/browser/js/browser.js b/web/pgadmin/browser/templates/browser/js/browser.js
index 93d30c0db..f44b7db96 100644
--- a/web/pgadmin/browser/templates/browser/js/browser.js
+++ b/web/pgadmin/browser/templates/browser/js/browser.js
@@ -443,25 +443,44 @@ OWNER TO helpdesk;\n';
if (d) {
/* Loading all the scripts registered to be loaded on this node */
if (obj.scripts && obj.scripts[d._type]) {
- _.each(obj.scripts[d._type], function(s) {
- if (!s.loaded) {
- require([s.name], function(m) {
- s.loaded = true;
- // Call the initialize (if present)
- if (m && m.init && typeof m.init == 'function') {
- try {
- m.init();
- } catch (err) {
- obj.report_error(
- '{{ _('Error Initializing script - ') }}' + s.path, err);
+ var scripts = _.extend({}, obj.scripts[d._type]);
+
+ /*
+ * We can remove it from the Browser.scripts object as
+ * these're about to be loaded.
+ *
+ * This will make sure that - we do check for the script for
+ * loading only once.
+ *
+ */
+ delete obj.scripts[d._type];
+
+ setTimeout(function() {
+ _.each(scripts, function(s) {
+ if (!s.loaded) {
+ require([s.name], function(m) {
+ s.loaded = true;
+ // Call the initialize (if present)
+ if (m && m.init && typeof m.init == 'function') {
+ try {
+ m.init();
+ } catch (err) {
+ console.log("Error running module Init script for '" + s.path + "'");
+ console.log(err);
+
+ obj.report_error(
+ '{{ _('Error Initializing script - ') }}' + s.path, err);
+ }
}
- }
- }, function() {
- obj.report_error(
- '{{ _('Error loading script - ') }}' + s.path);
- });
- }
- });
+ }, function() {
+ console.log("Error loading script - " + s.path);
+ console.log(arguments);
+ obj.report_error(
+ '{{ _('Error loading script - ') }}' + s.path);
+ }).bind(s);
+ }
+ });
+ }, 1);
}
}
break;
diff --git a/web/pgadmin/browser/templates/browser/js/node.js b/web/pgadmin/browser/templates/browser/js/node.js
index e097525ca..afcabf1b2 100644
--- a/web/pgadmin/browser/templates/browser/js/node.js
+++ b/web/pgadmin/browser/templates/browser/js/node.js
@@ -1005,7 +1005,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
Model: Backbone.Model.extend({
parse: function(res) {
var self = this;
- if ('node' in res && res['node']) {
+ if (res && _.isObject(res) && 'node' in res && res['node']) {
self.tnode = _.extend({}, res.node);
delete res.node;
}
@@ -1042,24 +1042,25 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
case 'model':
obj = self.get(s.id);
val = res[s.id];
- if (_.isArray(val) || _.isObject(val)) {
- if (!_.isObject(obj)) {
+ if (!_.isUndefined(val) && !_.isNull(val)) {
+ if (!obj || !(obj instanceof Backbone.Model)) {
if (_.isString(s.model) &&
s.model in pgBrowser.Nodes[s.model]) {
obj = new (pgBrowser.Nodes[s.model].Model)(
- null, {handler: self.handler || self}
+ obj, {silent: true, handler: self.handler || self}
);
} else {
- obj = new (s.model)(null, {handler: self.handler || self});
+ obj = new (s.model)(obj, {
+ silent: true, handler: self.handler || self
+ });
}
}
- obj.set(self.get(s.id), {parse: true, silent: true});
+ obj.set(val, {parse: true, silent: true});
} else {
if (obj)
delete obj;
obj = null;
}
- self.set(s.id, obj, {silent: true});
res[s.id] = obj;
break;
default:
@@ -1072,6 +1073,8 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
initialize: function(attributes, options) {
var self = this;
+ Backbone.Model.prototype.initialize.apply(self, arguments);
+
if (_.isUndefined(options) || _.isNull(options)) {
options = attributes || {};
attributes = null;
@@ -1088,31 +1091,35 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
if (self.schema && _.isArray(self.schema)) {
_.each(self.schema, function(s) {
- var obj = null;
+ var obj = self.get(s.id);
switch(s.type) {
case 'collection':
- if (_.isString(s.model) &&
+ if (!obj || !(obj instanceof pgBrowser.Node.Collection)) {
+ if (_.isString(s.model) &&
s.model in pgBrowser.Nodes) {
- var node = pgBrowser.Nodes[s.model];
- obj = new (node.Collection)(null, {
- model: node.model,
- handler: self.handler || self
- });
- } else {
- obj = new (pgBrowser.Node.Collection)(null, {
- model: s.model,
- handler: self.handler || self
- });
+ var node = pgBrowser.Nodes[s.model];
+ obj = new (node.Collection)(obj, {
+ model: node.model,
+ handler: self.handler || self
+ });
+ } else {
+ obj = new (pgBrowser.Node.Collection)(obj, {
+ model: s.model,
+ handler: self.handler || self
+ });
+ }
}
break;
case 'model':
- if (_.isString(s.model) &&
- s.model in pgBrowser.Nodes[s.model]) {
- obj = new (pgBrowser.Nodes[s.model].Model)(
- null, {handler: self.handler || self}
- );
- } else {
- obj = new (s.model)(null, {handler: self.handler || self});
+ if (!obj || !(obj instanceof Backbone.Model)) {
+ if (_.isString(s.model) &&
+ s.model in pgBrowser.Nodes[s.model]) {
+ obj = new (pgBrowser.Nodes[s.model].Model)(
+ obj, {handler: self.handler || self}
+ );
+ } else {
+ obj = new (s.model)(obj, {handler: self.handler || self});
+ }
}
break;
default:
@@ -1148,8 +1155,12 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
var self = this;
return (_.size(self.sessAttrs) > 0 ||
- _.some(self.objects, function(o) {
- return self.get(o).sessChanged();
+ _.some(self.objects, function(k) {
+ var obj = self.get(k);
+ if (!(_.isNull(obj) || _.isUndefined(obj))) {
+ return obj.sessChanged();
+ }
+ return false;
}));
},
sessValid: function() {
@@ -1208,11 +1219,10 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
res = _.extend(res, self.sessAttrs);
}
- _.each(self.objects, function(o) {
- var obj = self.get(o);
- if (session || obj)
- res[o] = (obj && obj.toJSON(session));
- });
+ _.each(self.objects, function(k) {
+ var obj = self.get(k);
+ res[k] = (obj && obj.toJSON(session));
+ });
return res;
},
startNewSession: function() {
@@ -1229,6 +1239,10 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) {
_.each(self.objects, function(o) {
var obj = self.get(o);
+ if (_.isUndefined(obj) || _.isNull(obj)) {
+ return;
+ }
+
delete self.origSessAttrs[o];
if (obj && 'startNewSession' in obj && _.isFunction(obj.startNewSession)) {
diff --git a/web/pgadmin/browser/utils.py b/web/pgadmin/browser/utils.py
index 665bade23..590493e6a 100644
--- a/web/pgadmin/browser/utils.py
+++ b/web/pgadmin/browser/utils.py
@@ -57,6 +57,7 @@ class PGChildModule:
def __init__(self, *args, **kwargs):
self.min_ver = 1000000000
self.max_ver = 0
+ self.server_type = None
self.attributes = {}
super(PGChildModule, self).__init__(*args, **kwargs)
diff --git a/web/pgadmin/static/css/overrides.css b/web/pgadmin/static/css/overrides.css
index df5f3301b..04b22245d 100644
--- a/web/pgadmin/static/css/overrides.css
+++ b/web/pgadmin/static/css/overrides.css
@@ -492,7 +492,11 @@ fieldset[disabled] .form-control {
}
.backgrid td.renderable:not(.editable):not(.delete-cell) {
- background-color: #eee;
+ background-color: #F1F1F1;
+}
+
+.backgrid tr.header td.renderable:not(.editable):not(.delete-cell) {
+ background-color: #AAA;
}
.subnode-header {
@@ -583,3 +587,11 @@ table.backgrid tr.new {
.switch-cell {
height: 0px; width: 0px;
}
+
+.width_percent_40 {
+ width: 40%;
+}
+
+.width_percent_60 {
+ width: 60%;
+}
diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js
index 23dec7e1e..f8e3ca160 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -36,7 +36,8 @@
var pgAdmin = (window.pgAdmin = window.pgAdmin || {});
pgAdmin.editableCell = function() {
- if (this.attributes && this.attributes.disabled) {
+ if (this.attributes && !_.isUndefined(this.attributes.disabled) &&
+ !_.isNull(this.attributes.disabled)) {
if(_.isFunction(this.attributes.disabled)) {
return !(this.attributes.disabled.apply(this, arguments));
}
@@ -503,14 +504,19 @@
initialize: function() {
Backform.Control.prototype.initialize.apply(this, arguments);
- var uniqueCol = this.field.get('uniqueCol') || [];
+ var uniqueCol = this.field.get('uniqueCol') || [],
+ m = this.field.get('model'),
+ schema = m.prototype.schema || m.__super__.schema,
+ columns = [];
+
+ _.each(schema, function(s) {
+ columns.push(s.id);
+ });
- var columns = this.field.get('columns')
// Check if unique columns provided are also in model attributes.
if (uniqueCol.length > _.intersection(columns, uniqueCol).length){
errorMsg = "Developer: Unique column/s [ "+_.difference(uniqueCol, columns)+" ] not found in collection model [ " + columns +" ]."
alert (errorMsg);
- return null;
}
var collection = this.model.get(this.field.get('name')),
@@ -747,7 +753,8 @@
var subnode = data.subnode.schema ? data.subnode : data.subnode.prototype,
gridSchema = Backform.generateGridColumnsFromModel(
data.node_info, subnode, this.field.get('mode'), data.columns
- );
+ ), self = this,
+ pgBrowser = window.pgAdmin.Browser;
// Set visibility of Add button
if (data.disabled || data.canAdd == false) {
@@ -775,7 +782,17 @@
});
}
- var collection = this.model.get(data.name);
+ var collection = self.model.get(data.name);
+
+ if (!collection) {
+ collection = new (pgBrowser.Node.Collection)(null, {
+ handler: self.model.handler || self,
+ model: data.model,
+ silent: true
+ });
+ self.model.set(data.name, collection, {silent: true});
+ }
+
// Initialize a new Grid instance
var grid = new Backgrid.Grid({
columns: gridSchema.columns,
diff --git a/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js b/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
index 74f769062..b12c089c3 100644
--- a/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
+++ b/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
@@ -119,254 +119,6 @@
}
});
-
-/**
- Custom cell formatter for privileges.
- */
-var PrivilegeCellFormatter = Backgrid.Extension.PrivilegeCellFormatter = function () {};
-_.extend(PrivilegeCellFormatter.prototype, {
-
- fromRaw: function (rawData, model) {
- return rawData;
- },
-
-
- /* Convert string privileges to object privileges for manipulation.
-
- E.g C*Tc ===> {"C":{"privilege":true,
- "withGrantPrivilege":true},
- "T":{"privilege":true,
- "withGrantPrivilege":false},
- "c":{"privilege":true,
- "withGrantPrivilege":false}
- }
- */
-
- fromRawToObject: function (rawData, model) {
- var objData = {};
- var currentChar = "";
- for (var i = 0, len = rawData.length; i < len; i++) {
- if (rawData[i] == "*" && currentChar != ""){
- if ( _.has(objData,currentChar)){
- objData[currentChar]["withGrantPrivilege"] = true;
- }
- }else{
- currentChar = rawData[i]
- objData[currentChar] = {"privilege":true,
- "withGrantPrivilege":false};
- }
- }
- return objData;
- },
-
- toRaw: function (formattedData, model) {
- return formattedData;
- }
-
-});
-
-/**
- Custom cell editor for editing privileges.
- */
-
-var PrivilegeCellEditor = Backgrid.Extension.PrivilegeCellEditor = Backgrid.CellEditor.extend({
- tagName: "div",
- template: _.template(['',
- ' | ',
- ' | ',
- '
'].join(" "), null, {variable: null}),
-
- initialize: function() {
- Backgrid.CellEditor.prototype.initialize.apply(this, arguments);
- this.elId = _.uniqueId('pgPriv_');
- },
- setPrivilegeOptions: function (privilegeOptions){
- this.privilegeOptions = privilegeOptions;
- },
-
- render: function () {
- this.$el.empty();
- this.$el.attr('tabindex', '1');
- this.$el.attr('id', this.elId);
- this.$el.attr('privilegeseditor', '1');
-
- var privilegeOptions = _.result(this, "privilegeOptions");
- var model = this.model;
- var selectedValues = this.formatter.fromRawToObject(model.get(this.column.get("name")), model),
- tbl = $("").appendTo(this.$el);
-
- if (!_.isArray(privilegeOptions)) throw new TypeError("privilegeOptions must be an array");
- self = this;
- // For each privilege generate html template.
- _.each(privilegeOptions, function (privilegeOption){
- var templateData = {name: privilegeOption['name'],
- value: privilegeOption['value'],
- privilege : false,
- withGrantPrivilege : false
- };
-
- if ( _.has(selectedValues,privilegeOption['value'])){
- _.extend(templateData,{ privilege:selectedValues[privilegeOption['value']]["privilege"],
- withGrantPrivilege:selectedValues[privilegeOption['value']]["withGrantPrivilege"]
- });
- }
-
- var editorHtml = self.template(templateData);
- tbl.append(editorHtml);
-
- var $prvilegeGrantCheckbox = self.$el.find("[name='" + privilegeOption['value'] + "_grant']");
-
- // Add event listeners on each privilege checkbox. And set initial state.
- // Update model if user changes value.
- $prvilegeGrantCheckbox.click(function(e) {
- var addRemoveflag = $(this).is(':checked');
- privilege = this.name;
- self.updateModel(privilege, addRemoveflag);
- });
-
- var $prvilegeCheckbox = self.$el.find("[name='" + privilegeOption['value'] + "']");
-
- if (!$prvilegeCheckbox.is(':checked')) {
- $prvilegeGrantCheckbox.attr("disabled", true);
- $prvilegeGrantCheckbox.attr("checked", false);
- }
-
- $prvilegeCheckbox.click(function(e) {
- var addRemoveflag = $(this).is(':checked');
- privilege = this.name;
- if (addRemoveflag) {
- $prvilegeGrantCheckbox.removeAttr("disabled");
- } else {
- $prvilegeGrantCheckbox.attr("disabled", true);
- $prvilegeGrantCheckbox.attr("checked", false);
- }
- self.updateModel(privilege, addRemoveflag);
- });
- });
-
- self.$el.find('input[type=checkbox]').blur(self.focusLost.bind(this)).first().focus();
- self.delegateEvents();
- return this;
- },
- updateModel: function(privilege, addRemoveflag){
- // Update model with new privilege string. e.g. 'C*Tc'.
- var self = this,
- model = self.model,
- column = self.column,
- newVal = "",
- withGrant = false,
- privilegeConst = privilege[0];
-
- if (privilege.length > 1){
- withGrant = true;
- }
-
- oldValObj = self.formatter.fromRawToObject(model.get(self.column.get("name")), model);
-
- if (addRemoveflag){
- if (!withGrant){
- oldValObj[privilegeConst] = {"privilege": true,
- "withGrantPrivilege":false};
- }else{
- oldValObj[privilegeConst] = {"privilege": true,
- "withGrantPrivilege":true}
- }
- }else{
- if (!withGrant){
- oldValObj[privilegeConst] = {"privilege": false,
- "withGrantPrivilege":false};
- }else{
- oldValObj[privilegeConst] = {"privilege": true,
- "withGrantPrivilege":false};
- }
- }
-
- for (var i = 0, len = model.privileges.length; i < len; i++) {
- if ( _.has(oldValObj, model.privileges[i])){
- if(oldValObj[model.privileges[i]]["privilege"]){
- newVal = newVal + model.privileges[i]
- }
- if(oldValObj[model.privileges[i]]["withGrantPrivilege"]){
- newVal = newVal + "*"
- }
- }
- }
- model.set(column.get("name"), newVal);
- },
- focusLost :function(e) {
- setTimeout(
- function() {
- var lostFocus = true;
- if (document.activeElement) {
- lostFocus = !(
- $(document.activeElement).closest(
- 'div[privilegeseditor=1]'
- ).first().attr('id') == this.$el.attr('id')
- );
- }
- if (lostFocus) {
- this.model.trigger("backgrid:edited", this.model, this.column, new Backgrid.Command(e));
- }
- }.bind(this), 200);
- }
- });
-
- var PrivilegeCell = Backgrid.Extension.PrivilegeCell = Backgrid.Cell.extend({
- className: "edit-cell",
- // All available privileges.
- privilegeLabels: { "C": "CREATE",
- "T": "TEMP",
- "c": "CONNECT",
- "a": "INSERT",
- "r": "SELECT",
- "w": "UPDATE",
- "d": "DELETE",
- "D": "TRUNCATE",
- "x": "REFERENCES",
- "t": "TRIGGER",
- "U": "USAGE",
- "X": "EXECUTE"
- },
-
- formatter: PrivilegeCellFormatter,
-
- editor: PrivilegeCellEditor,
-
- initialize: function(options) {
- Backgrid.Cell.prototype.initialize.apply(this, arguments);
-
- var privilegeOptions = [];
- var privileges = this.model.privileges || [];
- self = this;
- // Generate array of privileges to be shown in editor.
- _.each(privileges, function(privilege){
- privilegeOptions.push({name:self.privilegeLabels[privilege],
- value:privilege})
- })
-
- this.listenTo(this.model, "backgrid:edit", function (model, column, cell, editor) {
- if (column.get("name") == this.column.get("name"))
- // Set available privilege options in editor.
- editor.setPrivilegeOptions(privilegeOptions);
- });
- },
-
- render: function(){
- this.$el.empty();
- var model = this.model;
- this.$el.text(this.formatter.fromRaw(model.get(this.column.get("name")), model));
- this.delegateEvents();
- if (this.grabFocus)
- this.$el.focus();
- return this;
- },
-
- exitEditMode: function() {
- Backgrid.Cell.prototype.exitEditMode.apply(this, arguments);
- this.render();
- }
- });
-
var ObjectCell = Backgrid.Extension.ObjectCell = Backgrid.Cell.extend({
editorOptionDefaults: {
schema: []