diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py
index 38693d4..e00b6a7 100644
--- a/web/pgadmin/browser/__init__.py
+++ b/web/pgadmin/browser/__init__.py
@@ -153,7 +153,8 @@ class BrowserModule(PgAdminModule):
         for name, end in [
                 ['pgadmin.browser.menu', 'js/menu'],
                 ['pgadmin.browser.panel', 'js/panel'],
-                ['pgadmin.browser.frame', 'js/frame']]:
+                ['pgadmin.browser.frame', 'js/frame'],
+                ['pgadmin.browser.wizard', 'js/wizard']]:
             scripts.append({
                 'name': name, 'path': url_for('browser.static', filename=end),
                 'preloaded': True})
diff --git a/web/pgadmin/browser/static/js/node.ui.js b/web/pgadmin/browser/static/js/node.ui.js
index b84b6ee..47f1266 100644
--- a/web/pgadmin/browser/static/js/node.ui.js
+++ b/web/pgadmin/browser/static/js/node.ui.js
@@ -279,7 +279,7 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) {
         return false;
       };
 
-      while(p) {
+      while(p && p.length > 0) {
         top = p.get(0).offsetTop + p.height();
         p = p.parent();
         if (hasScrollbar(p)) {
diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js
index 4346156..8f913d2 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -108,7 +108,7 @@
       return m[idx > len ? 0 : idx];
     }
     return type;
-  }
+  };
 
 
   var BackformControlInit = Backform.Control.prototype.initialize,
@@ -745,6 +745,13 @@
         self.stopListening(self.collection, "change", self.collectionChanged);
       }
 
+      // Remove grid
+      if (this.grid) {
+        this.grid.remove();
+        delete this.grid;
+        this.grid = null;
+      }
+
       Backform.Control.prototype.remove.apply(this, arguments);
     },
     collectionChanged: function(newModel, coll, op) {
@@ -1146,8 +1153,12 @@
       /*
        * We will listen to the tab change event to check, if the SQL tab has
        * been clicked or, not.
+       *
+       * Also, listen to the wizard next/prev page changed
        */
       this.model.on('pg-property-tab-changed', this.onTabChange, this);
+      this.model.on('pgadmin-wizard:nextpage:sql', this.onWizardNextPageChange, this);
+      this.model.on('pgadmin-wizard:prevpage:sql', this.onWizardPrevPageChange, this);
     },
     getValueFromDOM: function() {
         return this.formatter.toRaw(this.$el.find("textarea").val(), this.model);
@@ -1205,8 +1216,47 @@
         }
       }
     },
+
+    // This method fetches the modified SQL for the wizard
+    onWizardNextPageChange: function(obj){
+
+      var self = this,
+        m = self.model,
+        gid = m.node_info['server-group']._id,
+        sid = m.node_info.server._id,
+        did = m.node_info.database._id,
+        nspname = m.node_info.schema.label;
+
+        // generate encoded url based on wizard type
+        msql_url = "/wizard/"+obj.wizard_type+"/msql/"+
+          S('%s/%s/%s/%s/%s/').sprintf(
+              encodeURI(gid), encodeURI(sid), encodeURI(did),
+              encodeURI(obj.node_type), encodeURI(nspname)).value();
+
+      // Fetches modified SQL
+      $.ajax({
+        url: msql_url,
+        type: 'GET',
+        cache: false,
+        data: self.model.toJSON(true, 'GET'),
+        dataType: "json",
+        contentType: "application/json"
+      }).done(function(res) {
+        self.sqlTab.clearHistory();
+        self.sqlTab.setValue(res.data);
+      }).fail(function() {
+        self.model.trigger('pgadmin-view:msql:error');
+      }).always(function() {
+        self.model.trigger('pgadmin-view:msql:fetched');
+      });
+
+    },
     remove: function() {
       this.model.off('pg-property-tab-changed', this.onTabChange, this);
+
+      // Stop listen to next/prev page events
+      this.model.off('pgadmin-wizard:nextpage:sql', this.onWizardNextPageChange, this);
+      this.model.off('pgadmin-wizard:prevpage:sql', this.onWizardPrevPageChange, this);
       Backform.Control.__super__.remove.apply(this, arguments);
     }
 });
diff --git a/web/pgadmin/templates/base.html b/web/pgadmin/templates/base.html
index 6ea5d69..d91203a 100755
--- a/web/pgadmin/templates/base.html
+++ b/web/pgadmin/templates/base.html
@@ -28,6 +28,8 @@
         <link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/backgrid/backgrid-paginator.css' if config.DEBUG else 'css/backgrid/backgrid-paginator.min.css')}}"/>
         <link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/backgrid/backgrid-filter.css' if config.DEBUG else 'css/backgrid/backgrid-filter.min.css')}}"/>
         <link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/select2/select2.css' if config.DEBUG else 'css/select2/select2.min.css')}}"/>
+        <link type="text/css" rel="stylesheet" href="{{ url_for('grantwizard.static', filename='css/wizard.css') }}"/>
+        <link type="text/css" rel="stylesheet" href="{{ url_for('grantwizard.static', filename='css/grantwizard.css') }}"/>
 
         <!-- View specified stylesheets -->
         {% for stylesheet in current_app.stylesheets %}
diff --git a/web/pgadmin/tools/grantwizard/__init__.py b/web/pgadmin/tools/grantwizard/__init__.py
new file mode 100644
index 0000000..8b2e82b
--- /dev/null
+++ b/web/pgadmin/tools/grantwizard/__init__.py
@@ -0,0 +1,286 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Grant Wizard"""
+
+import json
+from flask import render_template, request, current_app
+from flask.ext.babel import gettext
+from pgadmin.utils.ajax import make_response as ajax_response, \
+    make_json_response, internal_server_error
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from pgadmin.browser.server_groups.servers.utils import parse_priv_to_db
+from pgadmin.utils import PgAdminModule
+from flask import Response, url_for
+from flask.ext.security import login_required
+
+# As unicode type is not available in python3
+# If we check a variable is "isinstance(variable, str)
+# it breaks in python 3 as variable type is not string its unicode.
+# We assign basestring as str type if it is python3, unicode
+# if it is python2.
+
+try:
+    unicode = unicode
+except NameError:
+    # 'unicode' is undefined, must be Python 3
+    str = str
+    unicode = str
+    bytes = bytes
+    basestring = (str, bytes)
+else:
+    # 'unicode' exists, must be Python 2
+    str = str
+    unicode = unicode
+    bytes = str
+    basestring = basestring
+
+MODULE_NAME = 'grantwizard'
+
+
+class GrantWizardModule(PgAdminModule):
+    """
+    class GrantWizardModule(Object):
+
+        It is a wizard which inherits PgAdminModule
+        class and define methods to load its own
+        javascript file.
+    """
+    def get_own_javascripts(self):
+        """Add grantwizard.js file to load it on window load"""
+        return [{
+            'name': 'pgadmin.grantwizard',
+            'path': url_for('grantwizard.index') + 'grantwizard',
+            'when': None
+        }
+        ]
+
+# Create blueprint for GrantWizardModule class
+blueprint = GrantWizardModule(
+    MODULE_NAME, __name__, static_url_path='',
+    url_prefix='/wizard')
+
+
+@blueprint.route("/")
+@login_required
+def index():
+    pass
+
+
+@blueprint.route("/grantwizard.js")
+@login_required
+def script():
+    """render own javascript"""
+    return Response(response=render_template(
+                "grantwizard/js/grantwizard.js", _=gettext),
+                status=200,
+                mimetype="application/javascript")
+
+
+@blueprint.route(
+    '/grantwizard/properties/<int:gid>/<int:sid>/<int:did>'
+    '/<int:node_id>/<node_name>/<node_type>/<nspname>/',
+    methods=('GET', 'POST'))
+@login_required
+def properties(gid, sid, did, node_id, node_name, node_type, nspname):
+    """It fetches the properties of object types
+       and render into selection page of wizard
+    """
+    res_data = []
+    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+    conn = manager.connection(did=did)
+
+    # we will set template path for sql scripts
+    template_path = 'grantwizard/sql'
+
+    # Fetch functions against schema
+    if node_type in ['schema']:
+        SQL = render_template("/".join(
+            [template_path, 'properties.sql']),
+            node_id=node_id, node_name=node_name,
+            type=node_type, nspname=nspname)
+
+        status, res = conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        res_data.extend(res['rows'])
+
+    # Fetch trigger functions
+    if node_type in ['schema', 'trigger_function']:
+        SQL = render_template("/".join(
+            [template_path, 'properties.sql']),
+            node_id=node_id, node_name=node_name,
+            type='trigger_function', nspname=nspname)
+        status, res = conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        res_data.extend(res['rows'])
+
+    # Fetch Sequences against schema
+    if node_type in ['schema', 'sequence']:
+        SQL = render_template("/".join(
+            [template_path, 'properties.sql']),
+            node_id=node_id, node_name=node_name,
+            type='sequence', nspname=nspname)
+
+        status, res = conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+        res_data.extend(res['rows'])
+
+    # Fetch Tables against schema
+    if node_type in ['schema', 'table']:
+        SQL = render_template("/".join(
+            [template_path, 'properties.sql']),
+            node_id=node_id, node_name=node_name,
+            type='table', nspname=nspname)
+
+        status, res = conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        res_data.extend(res['rows'])
+
+    # Fetch Views against schema
+    if node_type in ['schema', 'coll-view']:
+        SQL = render_template("/".join(
+            [template_path, 'properties.sql']),
+            node_id=node_id, node_name=node_name,
+            type='coll-view', nspname=nspname)
+
+        status, res = conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        res_data.extend(res['rows'])
+
+    return ajax_response(
+            response=res_data,
+            status=200
+            )
+
+
+@blueprint.route(
+    '/grantwizard/msql/<int:gid>/<int:sid>/<int:did>/<node_type>/<nspname>/',
+    methods=('GET', 'POST'))
+def msql(gid, sid, did, node_type, nspname):
+    """
+    This function will return modified SQL
+    """
+    data = {}
+    for k, v in request.args.items():
+        try:
+            data[k] = json.loads(v)
+        except ValueError:
+            data[k] = v
+
+    # Form db connection
+    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+    conn = manager.connection(did=did)
+
+    # we will set template path for sql scripts
+    template_path = 'grantwizard/sql'
+
+    acls = []
+    try:
+        acls = render_template(
+            "/".join([template_path, 'allowed_acl.json'])
+            )
+        acls = json.loads(acls)
+    except Exception as e:
+        current_app.logger.exception(e)
+
+    try:
+        # Parse Privileges
+        if 'acl' in data:
+            data['acl'] = parse_priv_to_db(data['acl'], acls[node_type]['acl'])
+
+        # Pass Database Objects and get SQL for privileges
+        SQL = render_template("/".join(
+            [template_path, 'grant.sql']),
+            data=data, nspname=nspname, conn=conn)
+
+        res = {'data': SQL}
+
+        return ajax_response(
+            response=res,
+            status=200
+            )
+
+    except Exception as e:
+        return make_json_response(
+            status=410,
+            success=0,
+            errormsg=e.message
+        )
+
+
+@blueprint.route(
+    '/grantwizard/save/<int:gid>/<int:sid>/<int:did>/'
+    '<node_type>/<nspname>/',
+    methods=('GET', 'POST'))
+def save(gid, sid, did, node_type, nspname):
+    """
+    This function will apply the privileges to the selected
+    Database Objects
+    """
+    data = request.form if request.form else json.loads(request.data.decode())
+
+    # Form db connection and we use conn to execute sql
+    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+    conn = manager.connection(did=did)
+
+    # we will set template path for sql scripts
+    template_path = 'grantwizard/sql'
+
+    acls = []
+    try:
+        acls = render_template(
+            "/".join([template_path, 'allowed_acl.json'])
+            )
+        acls = json.loads(acls)
+    except Exception as e:
+        current_app.logger.exception(e)
+
+    try:
+
+        # Parse privileges
+        if 'acl' in data:
+            data['acl'] = parse_priv_to_db(
+                data['acl'],
+                acls[node_type]['acl']
+                )
+
+        # Pass Database Objects and get SQL for privileges
+        SQL = render_template("/".join(
+            [template_path, 'grant.sql']),
+            data=data, nspname=nspname, conn=conn)
+
+        if SQL and isinstance(SQL, basestring) and \
+                SQL.strip('\n') and SQL.strip(' '):
+                status, res = conn.execute_dict(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="Privileges Applied"
+                )
+
+    except Exception as e:
+        return make_json_response(
+            status=410,
+            success=0,
+            errormsg=e.message
+        )
diff --git a/web/pgadmin/tools/grantwizard/static/css/grantwizard.css b/web/pgadmin/tools/grantwizard/static/css/grantwizard.css
new file mode 100644
index 0000000..953e788
--- /dev/null
+++ b/web/pgadmin/tools/grantwizard/static/css/grantwizard.css
@@ -0,0 +1,48 @@
+/* Grant Wizard CSS */
+
+/* CSS to make Db object type table
+ * fixed so that tbody content may
+ * scroll
+ */
+.object_type_table thead tr {
+  position: relative;
+  display: block;
+}
+
+.object_type_table tbody {
+  display: block;
+  overflow-y: scroll;
+  height: 160px;
+}
+
+.object_type_table tbody tr td:nth-child(1),
+.object_type_table thead tr th:nth-child(1) {
+  width: 28px;
+  min-width: 28px;
+}
+
+.object_type_table tbody tr td:nth-child(2),
+.object_type_table thead tr th:nth-child(2) {
+  width: 185px;
+  min-width: 185px;
+}
+
+.object_type_table tbody tr td:nth-child(3),
+.object_type_table thead tr th:nth-child(3) {
+  width: 110px;
+  min-width: 110px;
+}
+
+.object_type_table tbody tr td:nth-child(4),
+.object_type_table thead tr th:nth-child(4) {
+  width: 244px;
+}
+
+.error_msg_div {
+	display:block;
+}
+
+/** Override Backgrid filter CSS **/
+.backgrid-filter.form-search {
+  margin: 0px 5px 5px 0;
+}
diff --git a/web/pgadmin/tools/grantwizard/static/css/wizard.css b/web/pgadmin/tools/grantwizard/static/css/wizard.css
new file mode 100644
index 0000000..058afa7
--- /dev/null
+++ b/web/pgadmin/tools/grantwizard/static/css/wizard.css
@@ -0,0 +1,86 @@
+/** CSS for Wizard **/
+.wizard_dlg {
+	height: 300px;
+	padding: 0 10px;
+}
+
+.ajs-content {
+	padding-top: 0px !important;
+}
+
+/* Wizard Header CSS */
+.wizard-header {
+  background: #428bca;
+	padding-left: 15px;
+	padding-bottom: 7px;
+	color: #fff;
+	font-size: 18px;
+	margin-bottom: 15px;
+	height: 76px;
+	line-height: 76px;
+}
+
+.wizard-header h3 {
+  font-size: 18px;
+  display: inline-block;
+}
+
+.wizard-content {
+	overflow-y: auto;
+	float: left;
+	padding: 0;
+	min-height: 280px;
+	max-height: 280px;
+}
+
+/* Wizard Footer CSS */
+.footer {
+  position: absolute;
+  border-top: 1px solid #ccc;
+  bottom: 0px;
+  height: 71px;
+  right: 0px;
+  padding-top: 22px;
+}
+
+/* Wizard Button CSS */
+.wizard-buttons {
+  float: right;
+}
+
+.wizard-buttons button {
+  float: left;
+  padding: 7px 15.2px;
+  font-size: 14px;
+  margin-right: 5px;
+}
+
+.wizard-finish {
+  margin-right: 0;
+}
+
+/* Wizard Status bar CSS */
+.wizard-status-bar {
+  height: 50px;
+  padding-bottom: 10px;
+  margin-bottom: 10px;
+  padding-left: 15px;
+}
+
+/* Error message css */
+.error_msg_div,
+.error_msg_div .pg-prop-status-bar {
+  background: #fff;
+  display: none;
+}
+
+/* In wizard select2 dropdown doesn't
+ * popup because z-index of alertify
+ * wizard is greater than the z-index
+ * of select2 dropdown. To make select2
+ * visible, set z-index of select2
+ * higher value than wizard's
+ */
+.select2-container--open {
+  z-index: 10000;
+}
diff --git a/web/pgadmin/tools/grantwizard/static/img/coll-extension.png b/web/pgadmin/tools/grantwizard/static/img/coll-extension.png
new file mode 100644
index 0000000000000000000000000000000000000000..eed7ca97a33ef595f448b8621168d531f86d51d5
GIT binary patch
literal 1017
zcmV<V0|xwwP)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp)
z=>Px%_fSk!MS^xZ(#^N3p?R&Od+_Yu$ibj%T^6RorB#bmn!1_5x}2JiUH9|m=i$p{
zRt>7esZx(rkHMB&m|kp}YkF-i+S9qHoNTYCfx^|oK7c>H>Bx7neMEjjfO9vZnQgzg
znaszo-`vc-ww7K_0rBeAwyS=gm1jPCJeJI$Gj=n&vyq*XV~~VSeQ+?GkzLu)v+(KC
z?&Zpui%@D?5_o7K5L*te<Gd|$E&TiU`}p$t_U!fW;PdR+>*2!a-MZ%4vg6gM;L)L=
zk53w479M3BDQ+lfsBo&|xoE3!8)F$jd_LXGn5dUmHEJcE$)m~b(xK3*v*EqB=fkk&
zzN6u@o!_pI*P(X7g>k=tf76xP$dIj?SVe9ugSLpn>&}+lsF2#DiP)Wj)|h<Mlh449
zm4!uCPy}dL4Vs8UtCv!Hv4M5cjedPPC}$ujZ6{xNJBocXBx4(;)vk8Mhd_@=5nK-#
zWf(t|O0=YEF=rrzvxn93+^yBLT$f*Ao@PIeN0+#kWR+urcQMcJ*SF-sT&rnflVFjw
zkaKM%G=n;@$*_-qITl_M!syGG&7n||RE^1*D|;}$)4tfmhrz6AIbIZAnqq{yi*T%V
zHhVWtV;&u18I#tdIE_NpzkbrYc9VfMLtPX<ZYx1&B5Z9Om3}8VZ7FiPes9o-TDx%1
zw{6X{Wx%FXorW{5k~_krMv{3VMsze|t!`+}gJR5mHj+XnY9(-;Zb*VhCx$c1uU*Hh
zRmG=Gz@R^havV*1JY33nR>*Toyk;|+MLMZbK)_+XojHAH5m<yvR;FT5#&1i+YDL0k
zJHK6_k4uhwGlp|1i+C)!pi;J+M~ZYIf@~RtZ5D4_30jF#R;gq*y;+2KJ*J&=$it|K
zbtb^1PrI~}#=)h;zM@}|T^wm05?&D+Y8^J0MJ<m!6k`>&nL&4HC>d%SP=`*4d_$3X
zCoGaZfT(|KS__tZB7UZQKD%8OXc&KL7J+LO8+Rm0f=C!{9T#mJ7UDFE00001bW%=J
z06^y0W&i*H0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_000McNliru+XEXB
z6Cp(Rbcp}}0B%V{K~xyiU68R2z#t3+nS(tt!3~hY^vGg42FL=`EENNm0{M!6-y1;)
zQxFazvL_cK*b<_okFg0d8Hc#V=Eh`)h+yZmplehnlNZEdtgQ@hX0CNeIWEpxw!oyN
ndc~tsp9TlidOzOC&-)*|KH)3pD2ngr00000NkvXXu0mjf<KNUz

literal 0
HcmV?d00001

diff --git a/web/pgadmin/tools/grantwizard/static/img/extension.png b/web/pgadmin/tools/grantwizard/static/img/extension.png
new file mode 100644
index 0000000000000000000000000000000000000000..e3c533347883fc1dec73bcb21b32fc54e3c31732
GIT binary patch
literal 996
zcmV<A0~`E_P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp)
z=>Px%`cO<%MQ2zLf_6I6&9|zdd99>-@a*2m!Junh7N)|bRf|-bx|zVboSKha_w(lG
z;mc-L4XVVcQjb)R!IoN>UTm6cdTlP+)48aeY_F(+!qvh)fIq$I$ak@QM1Demb2p-y
zZNIpg%*U?Z+|0eUmR?Q)@#@yLtA3u9XFhv8mdv0tb~C!Ok)4xckc3Wsa4?*aUD?pH
z@afX-<;s|gP-<HecxWLITMn+{ye)Dq{QLL&`11Mo?Dg>A^X%E{;lk+My5`xk<JGC)
z(V?J^Pa0qr9%URUZYXJ}aH`|EXsd7=V;MkvKHbcisFznYY9*e@qsi^kq0p+c;k~!#
z!?5MPqv5li->#6?p?1NAale3n)0NuDkgb|nMQ$vEwur;)&X(P%klLe(*qwsbn0(Ze
z&%lqBg+*3S1ZY?dnutTImr{GNfpyZ2etkM9XCNqTCtr9wihVOAV;iN_u6D(TK#xcf
zTn`v!7(bOtw4`b=XCQ>Lht=`it<|(#mtSF?W<QQcm$;T>m1BZ;G0*SUx8%WGt7&4B
zV3D+tb8RIwgF3Luu#bN^7G4v==*yVRp-_@kjmeoSdoaG!zSzWv!K`UHUKC!MVuZSj
zaIAJVdpAyF9vxyClh&g+jY8GGe$u;klYuouT@*iVD?w)>Y;7EsekVC?DRR1gZ_tQZ
zyKv99ZOyY~z@}84hBK{_JHn(!l6fLVbTnhFZfMSfV$6Lul0qhGC2*Z?NP<TvhBL~q
zUB|0c#ivccpg)Ro98G#WT*`P>$a6}(W;2>aI;l}Wz+t|fIeljlScFSfreaXWZ%f2#
zMZ#t~zg?k^OOAUphI1*4cr3V}Qns8&igY1@Y#D@Y7H?b$T8UCtsbn_2S%i2!rk!)h
z!>EaMCcvamyR?(W!KK8$qF<3+9BCaAUJ)8<9X6LmEss1DV->ZTL3e2=8EP9)hfaum
zLy>wXERsEdsDEo(3zmE$ex`jsyImG&7=LOOfol~TcO*%INEmJ%7i}B|YRn1%0004W
zQchC<K<3zH00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA00(qQO+^RW
z0~-xG4!&HBF8}}lR!KxbR2b7^U?3j2xS;VvLXbtwTz~>0W=QhVA)yH_FkL{}1;huk
zpaKCQKz@=7s`{i97i0@vl2TS8w1C7?R&G7y;)1Nm<<OZkC{A-h<6}A<CjbDaP8qDe
SR_4e60000<MNUMnLSTaX-^L37

literal 0
HcmV?d00001

diff --git a/web/pgadmin/tools/grantwizard/templates/grantwizard/js/grantwizard.js b/web/pgadmin/tools/grantwizard/templates/grantwizard/js/grantwizard.js
new file mode 100644
index 0000000..553a16a
--- /dev/null
+++ b/web/pgadmin/tools/grantwizard/templates/grantwizard/js/grantwizard.js
@@ -0,0 +1,863 @@
+define([
+      'jquery', 'underscore', 'underscore.string', 'alertify',
+      'pgadmin.browser', 'backbone', 'backgrid', 'pgadmin.browser.node',
+      'backgrid.select.all', 'backgrid.filter', 'backbone.paginator',
+      'backgrid.paginator', 'pgadmin.browser.server.privilege',
+      'pgadmin.browser.wizard',
+      ],
+
+function($, _, S, alertify, pgBrowser, Backbone, Backgrid, pgNode) {
+
+  if (pgBrowser.GrantWizard) {
+    return pgBrowser.GrantWizard;
+  }
+
+  /**
+    It is sub model for field "Objects". It has fields
+    for database object types such as Schemas, Views and
+    Sequence etc.
+  */
+  var DatabaseObjectModel = pgNode.Model.extend({
+    defaults: {
+      selected: false,
+      icon: 'icon-unknown',
+      name: undefined,
+      nspname: undefined,
+      proargs: undefined,
+      object_type: undefined,
+      object_id: undefined
+    },
+    idAttribute: 'object_id',
+    parse: function(res) {
+
+      // Create unique object id
+      res.object_id = res.object_type + res.nspname + '"' + res.name;
+
+      return res;
+    },
+
+    validate: function() {
+
+      /*
+      * Triggers error messages for object types "selected"
+      * if it is empty/undefined/null
+      */
+      var err = {},
+        errmsg,
+        node = this.get('objects').toJSON();
+      if (_.isEmpty(node)) {
+        err['selected'] = '{{ _("Please select any database object type") }}';
+        errmsg = errmsg || err['selected'];
+        this.errorModel.set('selected', errmsg);
+        return errmsg;
+      } else {
+        this.errorModel.unset('selected');
+      }
+      return null;
+    }
+  });
+
+  // Define privileges for various types of nodes
+  var privDict = {
+    'schema': ['a', 'r', 'w', 'd', 'D', 'x', 't', 'U', 'X'],
+    'coll-function': ['X'],
+    'coll-sequence': ['a', 'w', 'U'],
+    'coll-table':['a', 'w', 'U', 'd', 'D', 'x', 't'],
+    'coll-view': ['a', 'r', 'w', 'd', 'D', 'x', 't'],
+  };
+
+  // Define columns for the Db Object Types Grid
+  var columns = [{
+    name: "selected",
+
+    /*
+     Override render method of Backgrid.Extension.SelectRowCell
+     class. It has an issue: It doesn't mark rows checked if we move to next
+     page and then go back to previous page. but it must show.
+     so we handle this case by overriding the render method.
+    */
+    cell: Backgrid.Extension.SelectRowCell.extend({
+      render: function() {
+
+        // Use the Backform Control's render function
+        Backgrid.Extension.SelectRowCell.prototype.render.apply(this, arguments);
+
+        var col = this.column.get('name');
+        if (this.model && this.model.has(col)) {
+          if (this.model.get(col)) {
+            this.checkbox().prop("checked", true);
+            this.$el.parent().toggleClass("selected", true);
+            this.model.trigger("backgrid:selected", this.model, true);
+          }
+        }
+        return this;
+      }
+    }),
+
+    headerCell: "select-all",
+
+  },{
+    name: "object_type",
+    label: "Object Type",
+    editable: false,
+    cell: Backgrid.Cell.extend({
+      render: function() {
+
+        // Override render to add icon to Db Object column
+        Backgrid.Cell.prototype.render.apply(this, arguments);
+        this.$el.addClass(this.model.get('icon')).css({"padding-left": "22px"});
+
+        return this;
+      }
+    })
+  },{
+    name: "nspname",
+    label: "Schema",
+    cell: "string",
+    editable: false
+  },{
+    name: "name",
+    label: "Name",
+    cell: "string",
+    editable: false
+  }];
+
+  // Create an Object GrantWizard of pgBrowser class
+  pgBrowser.GrantWizard  = {
+    init: function() {
+      if (this.initialized) {
+        return;
+      }
+
+      this.initialized = true;
+
+      // Define list of nodes on which grant wizard option appears
+      var supported_nodes = [
+            'schema', 'function', 'coll-function',
+            'sequence', 'coll-sequcene',
+            'table', 'coll-table',
+            'view', 'coll-view'
+          ],
+
+          /**
+            Enable/disable grantwizard menu in tools based
+            on node selected
+            if selected node is present in supported_nodes,
+            menu will be enabled otherwise disabled.
+           */
+          menu_enabled = function(obj) {
+            if(!_.isUndefined(obj) && !_.isNull(obj))
+              return (_.indexOf(supported_nodes, obj._type) !== -1 ? true: false);
+            else
+              return false;
+          };
+
+      // Define the nodes on which the menus to be appear
+      var menus = [{
+        name: 'grant_wizard_schema', module: this,
+        applies: ['tools'], callback: 'start_grant_wizard',
+        priority: 10, label: '{{_("Grant Wizard...") }}',
+        icon: 'wcTabIcon', enable: menu_enabled
+      }];
+
+      // Add supported menus into the menus list
+      for (var idx = 0; idx < supported_nodes.length; idx++) {
+        menus.push({
+          name: 'grant_wizard_schema_context_' + supported_nodes[idx],
+          node: supported_nodes[idx], module: this,
+          applies: ['context'], callback: 'start_grant_wizard',
+          priority: 10, label: '{{_("Grant Wizard...") }}',
+          icon: 'wcTabIcon', enable: menu_enabled
+          });
+      }
+      pgAdmin.Browser.add_menus(menus);
+
+      return this;
+    },
+
+    // Callback to draw Wizard Dialog
+    start_grant_wizard: function(action, item) {
+      if (!alertify.wizardDialog) {
+        alertify.dialog('wizardDialog', function factory() {
+          return {
+            main:function(title) {
+              this.set('title', title);
+            },
+            setup:function() {
+              return {
+                options: {
+                  frameless: true,
+                  resizable: false,
+                  autoReset: false,
+                  maximizable: false,
+                  closableByDimmer: false
+                }
+              };
+            },
+            hooks:{
+              onshow: function() {
+
+                // Set dimensions for wizard
+                this.elements.dialog.style.width = '100%';
+                this.elements.dialog.style.height = '530px';
+              },
+            },
+            build:function() { },
+
+            /**
+              Returns a Collection used for rendering grid
+              and Pagination
+
+              @class {Backbone.PageableCollection}
+              @param {variable} baseUrl - from which data is fetched
+              @param {Backbone.Model} DatabaseObjectModel
+              @return {Object} coll
+            */
+            getPageableCollection: function(baseUrl){
+              var Coll = Backbone.PageableCollection.extend({
+                model: DatabaseObjectModel,
+                url: baseUrl,
+                mode: "client",
+                state: {
+                  pageSize: 100,
+                  sortKey: "object_type",
+                  order: -1
+                },
+                queryParams: {
+                  totalPages: null,
+                  totalRecords: null,
+                  sortKey: "sort"
+                }
+              });
+              return new Coll();
+            },
+
+            /**
+              Returns a Paginator Class Object which is again to be rendered
+
+              @class {Backgrid.Extension.Paginator}
+              @param {Backbone.Collection} coll - from which data is fetched
+              @return {Object} paginator
+            */
+            DbPaginator: function(coll){
+              var paginator = this.paginator = new Backgrid.Extension.Paginator({
+                collection: coll,
+                windowSize: 8
+              });
+              return paginator;
+            },
+
+            /**
+              Create new Filter which will filter the
+              rendered grid for Select Type Tabular Data
+              @param {Backbone.PageableCollection} coll
+            */
+            DbObjectFilter: function(coll){
+              var clientSideFilter = this.clientSideFilter = new Backgrid.Extension.ClientSideFilter({
+                collection: coll,
+                placeholder: _('Search by object type or name'),
+
+                // The model fields to search for matches
+                fields: ['object_type', 'name'],
+
+                // How long to wait after typing has stopped before searching can start
+                wait: 150
+              });
+              return clientSideFilter;
+            },
+
+            //Enable Disable Next button of Page 2
+            updateButtons: function(modified){
+              if(!modified)
+                $('.wizard-next').prop('disabled', true);
+              else
+                $('.wizard-next').prop('disabled', false);
+            },
+
+            /**
+              Callback called when an errorModel is set
+              with invalid value and errormsg is set into
+              status bar element and next button is disabled
+             */
+            onSessionInvalid: function(msg) {
+              $('.error_msg_div .pg-prop-status-bar').html(msg).css("display", "block");
+
+              // Enable disable Next button
+              this.updateButtons(false);
+              return true;
+            },
+
+            /**
+              Callback called when anything is set into model
+              thus hide error msg element and enable next button
+              status bar element and next button is disabled
+             */
+            onSessionValidated: function(sessHasChanged) {
+              $('.error_msg_div .pg-prop-status-bar').empty().css("display", "none");
+
+              // Enable disable Next button
+              this.updateButtons(sessHasChanged);
+            },
+
+            /*
+             Remove/Delete every object in wizard
+             on wizard close
+            */
+            releaseObjects: function(){
+
+              var self = this;
+
+              // Release/Delete every object
+              if(!_.isNull(self.dbObjectFilter)) {
+                self.dbObjectFilter.remove();
+                self.dbObjectFilter = null;
+              }
+
+              if(!_.isNull(self.clientSideFilter)) {
+                self.clientSideFilter.remove();
+                self.clientSideFilter = null;
+              }
+
+              // Remove paginator
+              if(!_.isNull(self.paginator)) {
+                self.paginator.remove();
+                self.paginator = null;
+              }
+
+              // Remove collection containing db object data
+              if(!_.isNull(self.coll)) {
+                self.coll.remove();
+                self.coll = null;
+              }
+
+            },
+
+            /**
+              Every time a wizard is opened, this function
+              is called everytime. It has Wizard Pages which
+              are rendered by the Wizard Class:
+
+              @class {pgBrowser.WizardPage} dbObjectType1 - This page
+              @extends {Backbone.Model}
+              renders a grid of Database Object Types such as
+                Schemas, Views and Sequences etc.
+
+              @class {pgBrowser.WizardPage} WizardPage2 - This page
+              @extends {Backbone.Model}
+              adds Privilege Control which provides grant privileges
+              such as "Create, Insert, Delete, Update" so on the
+              database objects selected on Wizard Pages.
+
+              @class {pgBrowser.WizardPage} WizardPage3 - This page
+              displays the generated GRANT SQL query for the Db
+              objects selected with the specific privileges added to it.
+              @extends {Backbone.Model}
+
+              @class {Backbone.Collection} WizardCollection - It is the
+              collection of wizard pages
+
+              @class {pgBrowser.Wizard} wizard - Its task is:
+              - Create a Wizard
+              - Add Buttons, Callbacks to it.
+              - Render WizardPages
+              @extends {Backbone.View}
+
+            */
+            prepare:function() {
+
+              // Remove extra container
+              if($('.ajs-content').size() > 1){
+                $('.ajs-content')[0].remove();
+              }
+
+              // Generate wizard main container
+              var wizard_html = '<div class="wizard_dlg">',
+                  wizard_parent = $('.ajs-content').empty().append(wizard_html),
+                  el = $(".ajs-content");
+
+              // Extract the data from the selected tree node
+              var t = pgBrowser.tree,
+                  i = t.selected(),
+                  d = i && i.length == 1 ? t.itemData(i) : undefined,
+                  info = pgBrowser.Node.getTreeNodeHierarchy(i),
+                  icon = d.icon;
+
+              /**
+                Generate a URL using:
+                gid, did, sid(server id), node_id(node id),
+                node_(node name), node_type(node type)
+                and pass it to collection which will fetch Object Type properties.
+               */
+              var gid = info['server-group']._id,
+                  sid = info.server._id,
+                  did = info.database._id,
+                  node_id = d._id,
+                  nspname = info.schema.label;
+                  node_type = d._type,
+                  node_label = d.label,
+
+                  // Collection url to fetch Database object types for objects field
+                  baseUrl = "{{ url_for('grantwizard.index') }}" + "grantwizard/properties/" +
+                      S('%s/%s/%s/%s/%s/%s/%s/').sprintf(
+                          encodeURI(gid), encodeURI(sid), encodeURI(did),
+                          encodeURI(node_id), encodeURI(node_label),
+                          encodeURI(node_type), encodeURI(nspname)).value();
+
+                  // Model's save url
+                  saveUrl = "{{ url_for('grantwizard.index') }}" + "grantwizard/save/" +
+                      S('%s/%s/%s/%s/%s/').sprintf(
+                          encodeURI(gid), encodeURI(sid), encodeURI(did),
+                          encodeURI(node_type), encodeURI(nspname)).value(),
+
+                  // Create instances of collection, pagination and filter
+                  coll = this.coll = this.getPageableCollection(baseUrl),
+                  paginator = this.paginator = this.DbPaginator(coll),
+                  dbObjectFilter = this.dbObjectFilter = this.DbObjectFilter(coll);
+
+              /**
+                It is the main model with schema defined
+                Every time a new wizard is opened,
+                a new model should create.
+               */
+              var GrantWizardModel = pgNode.Model.extend({
+                defaults: {
+                  objects: undefined,
+                  acl: undefined
+                },
+                schema: [
+                  {
+                    id: 'objects', label: '{{ _("Objects") }}', model: DatabaseObjectModel,
+                    type: 'collection', group: 'Objects'
+                  },
+                  {
+                    id: 'acl', label: '{{ _("Privileges") }}', node: 'role',
+                    model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({
+                      privileges: privDict[node_type]
+                    }), uniqueCol : ['grantee', 'grantor'], editable: true,
+                    type: 'collection', group: 'Privileges', canAdd: true,
+                    canDelete: true, control: 'unique-col-collection'
+                  }
+                ],
+                urlRoot: saveUrl
+              });
+
+              /**
+                Create instance of GrantWizard Model, provide urlRoot
+                node_info object, Generate fields objects
+               */
+              var self = this,
+                  newModel = new GrantWizardModel({}, { node_info: info }),
+                  fields = Backform.generateViewSchema(
+                     info, newModel, 'create', d._type, d
+                     );
+
+              /**
+                Fetch data from server and set into grid
+                and show/hide progress bar
+               */
+              $('.wizard-progress-bar p').show();
+
+              coll.fetch({
+                success: function(collection, data) {
+                  $('.wizard-progress-bar p').html('');
+                },
+                reset: true
+              }, this);
+
+              /**
+                Override backgrid listener "backgrid:selected" to
+                Add/Remove model to/from objects collection
+               */
+              coll.on('backgrid:selected', function(model, selected) {
+                model.set('selected', selected);
+
+                /**
+                  if a row (checkbox) is checked, add that model
+                  into collection, when unchecked remove it from
+                  model.
+                 */
+                if(selected)
+                  newModel.get('objects').add(model, { silent: true });
+                else
+                  newModel.get('objects').remove(model);
+
+                // validate model on checkbox check/uncheck
+                var msg =  model.validate.call(newModel);
+
+                /**
+                  If no object type is selected, set error msg
+                  and disable next button, else enable next button
+                 */
+                if(msg)
+                  self.onSessionInvalid.call(self, msg);
+                else
+                  self.onSessionValidated.call(self, true);
+              });
+
+              // To track changes into model, start new session
+              newModel.startNewSession();
+
+              // Add event listener for privileges control
+              newModel.on('pgadmin-session:valid', self.onSessionValidated.bind(this));
+              newModel.on('pgadmin-session:invalid', self.onSessionInvalid.bind(this));
+
+              //////////////////////////////////////////////////////////////////////
+              //                                                                  //
+              //            Wizard Page for Db Object Type                        //
+              //                                                                  //
+              //////////////////////////////////////////////////////////////////////
+
+              /**
+                Create wizard page. It renders a grid of
+                Database Object Types such as
+                Schemas, Views and Sequences etc.
+                Set default values
+               */
+              var dbObjectTypePage = self.dbObjectTypePage = new pgBrowser.WizardPage({
+                id: 1,
+                page_title: 'Object Selection (step 1 of 3)',
+                disable_prev: true,
+                disable_next: true,
+                show_status_bar: _('Please select objects from the below list.'),
+                show_progress_bar: _('Please wait while fetching records...'),
+                model: newModel,
+                view: new (function() {
+
+                  // Set page Instance
+                  var pageView = this;
+
+                  _.extend(pageView, {
+
+                    // Remove grid if it is before render
+                    cleanup: function() {
+                      if (this.grid) {
+                        this.grid.remove();
+                        delete this.grid;
+                        this.grid = null;
+                      }
+
+                      // Remove grid element if exists
+                      if (this.el) {
+                        $(this.el).remove();
+                        delete this.el;
+                      }
+                    },
+
+                    // Delete grid before render
+                    grid: null,
+
+                    // Render the Page1
+                    render: function() {
+
+                      // Create a grid container
+                      var gridBody =
+                            $('<div><div id="grid_filter"></div></div>');
+
+                      // Remove grid if exits before render
+                      if (this.grid) {
+                        this.cleanup();
+                      }
+
+                      // Initialize a new Grid instance
+                      this.grid = new Backgrid.Grid({
+                        columns: _.clone(columns),
+                        collection: coll,
+                        className: "backgrid table-bordered object_type_table"
+                        });
+
+                      // Render selection Type grid and paginator
+                      gridBody.append(
+                        this.grid.render().$el
+                        ).append(self.paginator.render().el);
+
+                      // Render Search Filter
+                      gridBody.prepend(
+                        self.clientSideFilter.render().el);
+
+                      // Assign gridBody content to page element
+                      this.el = gridBody;
+
+                      /**
+                        Fetch selected models from collection and
+                        make rows checked in grid
+                       */
+                      newModel.get('objects').each(function(m) {
+                        var model = coll.get(m.get('object_id'));
+                        if (model) {
+                          coll.trigger('backgrid:selected', model, true);
+                        }
+                      });
+
+                      // Refresh grid to re render rows.
+                      coll.trigger('backgrid:refresh');
+
+                      return this;
+                    }
+                  });
+                }),
+
+                beforeNext: function(obj){
+                  var self = this,
+                      selectedRows = self.view.grid.getSelectedModels();
+                      obj.options.disable_next = true;
+
+                  /**
+                    Enable/Disable next button of page 2 if objects
+                    are present in model
+                   */
+                  if(newModel.get('acl').length > 0)
+                    obj.collection.at(1).set('disable_next', false);
+
+                  // Clean the view
+                  if (self.view) {
+                    self.view.cleanup();
+                    delete self.view;
+                    self.view = null;
+                  }
+                  return true;
+                },
+
+              });
+
+              //////////////////////////////////////////////////////////////////////
+              //                                                                  //
+              //            Wizard Page for Privilege Control                     //
+              //                                                                  //
+              //////////////////////////////////////////////////////////////////////
+
+              /**
+                Create Field Object which has properties like
+                node_data, node_info which is required for rendering
+                Privilege control
+               */
+              var privilegesField = new Backform.Field(fields[1].fields[0]);
+
+              // Wizard for Privelege control
+              var privilegePage = self.privilegePage = new pgBrowser.WizardPage({
+                id: 2,
+                page_title: _('Privileges Selection (step 2 of 3)'),
+                show_status_bar: _('Please select privileges for the selected objects.'),
+                disable_next: true,
+                model: newModel,
+
+                // Create a view function object
+                view: new (function() {
+                  var pageView = this;
+                    _.extend(pageView, {
+
+                      // Render Privelege control to generate its html markup
+                      render: function() {
+                        this.privControl = new (privilegesField.get('control')) ({
+                          field: privilegesField,
+                          model: newModel
+                        });
+                        return {el: this.privControl.render().$el};
+                      },
+
+                      // Remove the privilege control
+                      cleanup: function() {
+                        if (this.privControl) {
+                          this.privControl.remove();
+                          delete this.privControl;
+                          this.privControl = null;
+                        }
+                      }
+                    });
+                }),
+
+                beforePrev: function(wizardObj) {
+
+                  // Remove the privilege control
+                  if (this.view) {
+                    this.view.cleanup();
+                    delete this.view;
+                    this.view = null;
+                  }
+
+                  /**
+                    Enable/Disable next button of page 1 if objects
+                    are present in model
+                   */
+                  var objectsModel = newModel.get('objects');
+
+                  if(!_.isUndefined(objectsModel) && !_.isEmpty(objectsModel) &&
+                      objectsModel.length > 0) {
+                    wizardObj.collection.at(0).set('disable_next', false);
+
+                    // Don't show progress bar
+                    wizardObj.collection.at(0).set('show_progress_bar', '');
+                  }
+
+                  /**
+                    We're re-rendering the controls as they are deleted
+                    before heading to next page
+                    Refresh Backgrid to re-render the elements selected
+                    re-render paginator
+                    re-render Filter
+                   */
+                  newModel.trigger("backgrid:refresh", newModel, false);
+                  self.paginator.render();
+                  self.clientSideFilter.render();
+                  return true;
+                },
+
+                beforeNext: function() { return true; },
+
+                onNext: function(obj){
+
+                  // Remove the privilege control
+                  if (this.view) {
+                    this.view.cleanup();
+                    delete this.view;
+                    this.view = null;
+                  }
+
+                  // Enable finish button on Wizard Page 3
+                  self.wizard.options.disable_finish = false;
+
+                  // Triggers to get SQL queries data to render into the
+                  // Wiard SQL Tab Control
+                  this.model.trigger('pgadmin-wizard:nextpage:sql',
+                      {'wizard_type': 'grantwizard', 'node_type': node_type });
+                }
+              });
+
+
+              //////////////////////////////////////////////////////////////////////
+              //                                                                  //
+              //            Wizard Page for SQL Tab                               //
+              //                                                                  //
+              //////////////////////////////////////////////////////////////////////
+
+              //Create SqlField Object
+              var sqlField = new Backform.Field(
+                  {
+                    id: 'sqltab',
+                    label: 'Sql Tab',
+                    field: fields,
+                    control: Backform.SqlTabControl
+                  }),
+
+                /**
+                  Create sqlField view instance
+                  to render it into wizard page
+                 */
+                sqlControl = new (sqlField.get('control'))({
+                  field: sqlField,
+                  model: newModel
+                });
+
+              // Wizard for SQL tab control
+              var reviewSQLPage = self.reviewSQLPage = new pgBrowser.WizardPage({
+                id: 3,
+                page_title: _('Final (Review Selection) (step 3 of 3)'),
+                show_status_bar: _('Following query will be executed on the database'
+                              +' server for the selected objects, and privileges.'
+                              +' Please click on Finish to complete the process.'),
+
+                view: new(function() {
+
+                  // Render SqlTab control to generate its html markup
+                  var sqlTabHtml = sqlControl.render().$el;
+                  this.render = function() {
+                      return {el: sqlTabHtml};
+                    };
+                }),
+
+                beforePrev: function(wizardObj) {
+
+                  /**
+                    Enable next button on page 2
+                    if privilege model is not empty
+                    else disable next button
+                    */
+                  var aclModel = newModel.get('acl');
+
+                  if(!_.isUndefined(aclModel) && !_.isEmpty(aclModel) &&
+                      aclModel.length > 0)
+                    wizardObj.collection.at(1).set('disable_next', false);
+                  else
+                    wizardObj.collection.at(1).set('disable_next', true);
+
+                  return true;
+                },
+              });
+
+
+              // Create Wizard Collection of Wizard Pages
+              var WizardCollection = Backbone.Collection.extend({
+                model: pgBrowser.WizardPage
+              });
+
+              // It holds all the wizard pages to be rendered
+              this.wizardCollection = new WizardCollection(
+                [dbObjectTypePage, privilegePage, reviewSQLPage]
+              );
+
+              /**
+                Create wizard which has following operations:
+                - renders wizard pages
+                - defines the first page to render in wizard
+                - Save the model on finishbutton
+                - Remove wizard on cancel button
+                */
+              var wizard = self.wizard = new (pgBrowser.Wizard.extend({
+                options: {
+                  title: 'Grant Wizard', /* Main Wizard Title */
+                  width: '',
+                  height: '',
+                  curr_page: 0,
+                  show_left_panel: false,
+                  disable_finish: true
+                },
+                onFinish: function() {
+                  var m = newModel,
+                      d = m.toJSON('GET');
+
+                  if (d && !_.isEmpty(d)) {
+                    m.save({}, {
+                      attrs: d,
+                      validate: false,
+                      cache: false,
+                      success: function(res) {
+
+                        // Release wizard objects
+                        self.close();
+                      },
+                      error: function(m, jqxhr) {
+                        alertify.pgNotifier(
+                          "error", jqxhr,
+                          S(
+                            "{{ _('Error during saving properties - %%s!') }}"
+                            ).sprintf(jqxhr.statusText).value()
+                          );
+                      }
+                    });
+                  }
+                },
+                onCancel: function() {
+
+                  // Release wizard objects
+                  self.releaseObjects();
+                  self.close();
+                }
+              })) ({
+                collection: this.wizardCollection,
+                el: el,
+                model: newModel
+              });
+
+              // Render wizard
+              wizard.render();
+            }
+          };
+        });
+      }
+
+      // Call Grant Wizard Dialog
+      alertify.wizardDialog('');
+    }
+  };
+
+  return pgBrowser.GrantWizard;
+});
diff --git a/web/pgadmin/tools/grantwizard/templates/grantwizard/sql/allowed_acl.json b/web/pgadmin/tools/grantwizard/templates/grantwizard/sql/allowed_acl.json
new file mode 100644
index 0000000..cb73d2c
--- /dev/null
+++ b/web/pgadmin/tools/grantwizard/templates/grantwizard/sql/allowed_acl.json
@@ -0,0 +1,30 @@
+{# List of allowed privileges of Schema, table sequence, function and view #}
+{#
+    Format for allowed privileges are:
+    "node_name": {
+        "type": "name",
+        "acl": [...]
+    }
+#}
+{
+    "schema": {
+        "type": "SCHEMA",
+        "acl": ["a", "r", "w", "d", "D", "x", "t", "U", "X"]
+    },
+    "coll-table": {
+        "type": "TABLE",
+        "acl": ["a", "w", "U", "d", "D", "x", "t"]
+    },
+    "coll-sequence": {
+        "type": "SEQUENCE",
+        "acl": ["a", "w", "u"]
+    },
+    "coll-function": {
+        "type": "FUNCTION",
+        "acl": ["X"]
+    },
+    "coll-view": {
+        "type": "VIEW",
+        "acl": ["a", "r", "w", "d", "D", "x", "t"]
+    }
+}
diff --git a/web/pgadmin/tools/grantwizard/templates/grantwizard/sql/grant.sql b/web/pgadmin/tools/grantwizard/templates/grantwizard/sql/grant.sql
new file mode 100644
index 0000000..02ac1a7
--- /dev/null
+++ b/web/pgadmin/tools/grantwizard/templates/grantwizard/sql/grant.sql
@@ -0,0 +1,19 @@
+{#=====Grant Permissions on Database Objects Selected====#}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% import 'macros/functions/privilege.macros' as PRIVILEGE_FUNCTION %}
+{% if data.acl %}
+{% for obj in data.objects %}
+{% for priv in data.acl %}
+{% set obj_type = obj.object_type %}
+{% if obj_type.upper() != 'FUNCTION' and obj_type.upper() != 'TRIGGER FUNCTION' %}
+{% if obj_type.upper() == 'VIEW' %} {###### Views are also kind of table ######}
+{% set obj_type = 'TABLE' %}
+{% endif %}
+{{ PRIVILEGE.SET(conn, obj_type.upper(), priv.grantee, obj.name, priv.without_grant, priv.with_grant, nspname ) }}
+{% else %}
+{###### if object_type is Function then apply function marcros ######}
+{{ PRIVILEGE_FUNCTION.SET(conn, 'FUNCTION', priv.grantee, obj.name, priv.without_grant, priv.with_grant, nspname, obj.proargs)}}
+{% endif %}
+{% endfor %}
+{% endfor %}
+{% endif %}
diff --git a/web/pgadmin/tools/grantwizard/templates/grantwizard/sql/properties.sql b/web/pgadmin/tools/grantwizard/templates/grantwizard/sql/properties.sql
new file mode 100644
index 0000000..7a33c1f
--- /dev/null
+++ b/web/pgadmin/tools/grantwizard/templates/grantwizard/sql/properties.sql
@@ -0,0 +1,65 @@
+{#===================Fetch list of Database object types(Functions, Sequence, Tables or View)===================#}
+{% if type == 'schema' or type == 'trigger_function' and node_id and node_name %}
+{% set func_type = 'Trigger Function' if type == 'trigger_function' else 'Function' %}
+SELECT
+    pr.proname AS name,
+    pg_get_function_identity_arguments(pr.oid) AS proargs,
+    '{{ func_type }}' AS object_type,
+    '{{ nspname }}' AS nspname,
+    'icon-function' AS icon
+FROM pg_proc pr
+    JOIN pg_type typ ON typ.oid=prorettype
+    JOIN pg_namespace typns ON typns.oid=typ.typnamespace
+    JOIN pg_language lng ON lng.oid=prolang
+    LEFT OUTER JOIN pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass)
+WHERE proisagg = FALSE AND pronamespace = {{ node_id }}::oid
+    AND typname {{ 'NOT' if type != 'trigger_function' else '' }} IN ('trigger', 'event_trigger')
+ORDER BY proname
+{% endif %}
+
+{% if type == 'sequence' and node_id and node_name %}
+SELECT
+    cl.relname AS name,
+    'Sequence' AS object_type,
+    '{{ nspname }}' AS nspname,
+    'icon-sequence' AS icon
+FROM pg_class cl
+    LEFT OUTER JOIN pg_description des ON (des.objoid=cl.oid AND des.classoid='pg_class'::regclass)
+WHERE relkind = 'S' AND relnamespace  = {{ node_id }}::oid
+ORDER BY cl.relname
+{% endif %}
+
+{% if type == 'table' and node_id and node_name %}
+SELECT rel.relname AS name,
+    'Table' AS object_type,
+    '{{ nspname }}' AS nspname,
+    'icon-table' AS icon
+FROM pg_class rel
+    LEFT OUTER JOIN pg_tablespace spc ON spc.oid=rel.reltablespace
+    LEFT OUTER JOIN pg_description des ON (des.objoid=rel.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass)
+    LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
+    LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
+    LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
+WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ node_id }}::oid
+ORDER BY rel.relname
+{% endif %}
+
+{% if type == 'coll-view' and node_id and node_name %}
+SELECT
+    c.relname AS name,
+    'View' AS object_type,
+    '{{ nspname }}' AS nspname,
+    'icon-coll-view' AS icon
+FROM pg_class c
+    LEFT OUTER JOIN pg_tablespace spc ON spc.oid=c.reltablespace
+    LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+    LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid
+WHERE ((c.relhasrules AND (EXISTS (
+    SELECT r.rulename
+    FROM pg_rewrite r
+    WHERE ((r.ev_class = c.oid)
+        AND (bpchar(r.ev_type) = '1'::bpchar)) ))
+       ) OR (c.relkind = 'v'::char))
+AND c.relnamespace = {{ node_id }}::oid
+ORDER BY c.relname
+{% endif %}
