(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, Backgrid, 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; Backgrid = require('backgrid') || root.Backgrid; 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.Backgrid, root.alertify, root.pgAdmin.Browser.Node); } } (this, function(root, _, $, Backbone, Backform, Backgrid, 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 }, validate: function() { return null; } }); /** * 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 - Grantor who has given this permission. * + privileges - Privileges for that role. **/ var PrivilegeRoleModel = pgNode.PrivilegeRoleModel = pgNode.Model.extend({ defaults: { grantee: undefined, grantor: undefined, privileges: undefined }, keys: ['grantee', 'grantor'], /* * 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, editable: true, cellHeaderClasses: 'width_percent_40', node: 'role', disabled : function(column, collection) { if (column instanceof Backbone.Collection) { // This has been called during generating the header cell return false; } return !( this.node_info && this.node_info.server.user.name == column.get('grantor') ); }, transform: function(data) { var res = Backgrid.Extension.NodeListByNameCell.prototype.defaults.transform.apply( this, arguments ); res.unshift({label: 'public', value: 'public'}); return res; }, cell: Backgrid.Extension.NodeListByNameCell.extend({ initialize: function(opts) { var self = this, override_opts = true; // We would like to override the original options, because - we // should omit the existing role/user from the privilege cell. // Because - the column is shared among all the cell, we can only // override only once. if (opts && opts.column && opts.column instanceof Backbone.Model && opts.column.has('orig_options')) { override_opts = false; } Backgrid.Extension.NodeListByNameCell.prototype.initialize.apply( self, arguments ); // Let's override the options if (override_opts) { var opts = self.column.get('options'); self.column.set('options', self.omit_selected_roles); self.column.set('orig_options', opts); } var rerender = function (m) { var self = this; if ('grantee' in m.changed && this.model.cid != m.cid) { setTimeout( function() { self.render(); }, 50 ); } }.bind(this); // We would like to rerender all the cells of this type for this // collection, because - we need to omit the newly selected roles // form the list. Also, the render will be automatically called for // the model represented by this cell, we will not do that again. this.listenTo(self.model.collection, "change", rerender, this); this.listenTo(self.model.collection, "remove", rerender, this); }, // Remove all the selected roles (though- not mine). omit_selected_roles: function() { var self = this, opts = self.column.get('orig_options'), res = opts.apply(this), selected = {}, cid = self.model.cid, curr_user = self.model.node_info.server.user.name; var idx = 0; this.model.collection.each(function(m) { var grantee = m.get('grantee'); if (m.cid != cid && !_.isUndefined(grantee) && curr_user == m.get('grantor')) { selected[grantee] = m.cid; } }); res = _.filter(res, function(o) { return !(o.value in selected); }); this.model.collection.available_roles = {}; return res; } }), },{ id: 'privileges', label:'Privileges', type: 'collection', model: PrivilegeModel, group: null, cell: 'privilege', control: 'text', cellHeaderClasses: 'width_percent_40', disabled : function(column, collection) { if (column instanceof Backbone.Collection) { // This has been called during generating the header cell return false; } return !(this.node_info && this.node_info.server.user.name == column.get('grantor') || this.attributes.node_info.server.user.name == column.get('grantor')); } },{ id: 'grantor', label: 'Grantor', type: 'text', disabled: true, cell: 'node-list-by-name', node: 'role' }], /* * 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); if (_.isNull(attrs)) { this.set( 'grantor', opts && opts.top && opts.top.node_info && opts.top.node_info.server.user.name, {silent: true} ); } /* * Define the collection of the privilege supported by this model */ var self = this, models = self.get('privileges'), privileges = this.get('privileges') || {}; if (_.isArray(privileges)) { privileges = new (pgNode.Collection)( models, { model: PrivilegeModel, top: this.top || this, handler: this, silent: true, parse: false }); this.set('privileges', privileges, {silent: true}); } var privs = {}; _.each(self.privileges, function(p) { privs[p] = { 'privilege_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}); }); self.on("change:grantee", self.granteeChanged); privileges.on('change', function() { self.trigger('change:privileges', self); }); return self; }, granteeChanged: function() { var privileges = this.get('privileges'), grantee = this.get('grantee'); // Reset all with grant options if grantee is public. if (grantee == 'public') { privileges.each(function(m) { m.set("with_grant", false, {silent: true}); }); } }, toJSON: function(session) { if (session) { return pgNode.Model.prototype.toJSON.apply(this, arguments); } 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 }; }, validate: function() { var err = {}, errmsg = null, changedAttrs = this.sessAttrs, msg = undefined; if (_.isUndefined(this.get('grantee'))) { msg = window.pgAdmin.Browser.messages.PRIV_GRANTEE_NOT_SPECIFIED; this.errorModel.set('grantee', msg); errmsg = msg; } else { this.errorModel.unset('grantee'); } var anyPrivSelected = false; this.attributes['privileges'].each( function(p) { if (p.get('privilege')) { anyPrivSelected = true; } }); if (!anyPrivSelected) { msg = window.pgAdmin.Browser.messages.NO_PRIV_SELECTED; this.errorModel.set('privileges', msg); errmsg = errmsg || msg; } else { this.errorModel.unset('privileges'); } return errmsg; } }); /** 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": "TEMPORARY", "c": "CONNECT", "a": "INSERT", "r": "SELECT", "w": "UPDATE", "d": "DELETE", "D": "TRUNCATE", "x": "REFERENCES", "t": "TRIGGER", "U": "USAGE", "X": "EXECUTE" }, template: _.template([ '