Hi,

PFA updated patch for events trigger node.

-- 
*Harshal Dhumal*
*Software Engineer *



EenterpriseDB <http://www.enterprisedb.com>

On Fri, Mar 4, 2016 at 7:58 PM, Dave Page <dp...@pgadmin.org> wrote:

>
>
> On Fri, Feb 12, 2016 at 6:32 AM, Harshal Dhumal <
> harshal.dhu...@enterprisedb.com> wrote:
>
>> Hi,
>>
>> PFA patch event triggers node.
>>
>>
> Hi
>
> It looks like this has bit-rotted. Please update and re-submit - currently
> it doesn't list triggers in the tree if they're already present, and if you
> try to create a new one it fails to find any trigger functions. I see this
> in the console:
>
> 2016-03-04 14:24:01,759: INFO werkzeug: 127.0.0.1 - - [04/Mar/2016
> 14:24:01] "GET /browser/event_trigger/nodes/1/1/12403/ HTTP/1.1" 500 -
> Traceback (most recent call last):
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1836, in __call__
>     return self.wsgi_app(environ, start_response)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1820, in wsgi_app
>     response = self.make_response(self.handle_exception(e))
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1403, in handle_exception
>     reraise(exc_type, exc_value, tb)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1817, in wsgi_app
>     response = self.full_dispatch_request()
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1477, in full_dispatch_request
>     rv = self.handle_user_exception(e)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1381, in handle_user_exception
>     reraise(exc_type, exc_value, tb)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1475, in full_dispatch_request
>     rv = self.dispatch_request()
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1461, in dispatch_request
>     return self.view_functions[rule.endpoint](**req.view_args)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/views.py",
> line 84, in view
>     return self.dispatch_request(*args, **kwargs)
>   File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/utils.py", line 248,
> in dispatch_request
>     return method(*args, **kwargs)
>   File
> "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/event_triggers/__init__.py",
> line 118, in wrap
>     return f(*args, **kwargs)
>   File
> "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/event_triggers/__init__.py",
> line 142, in nodes
>     res.append(
> AttributeError: 'dict' object has no attribute 'append'
>
> NOTE: Please also check the code conforms to the checklist I added to the
> docs earlier:
> http://git.postgresql.org/gitweb/?p=pgadmin4.git;a=blob;f=docs/en_US/code-review.rst
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/__init__.py
new file mode 100644
index 0000000..8e71a6e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/__init__.py
@@ -0,0 +1,658 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+import re
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext
+from pgadmin.utils.ajax import make_json_response, \
+    make_response as ajax_response, internal_server_error
+from pgadmin.browser.utils import PGChildNodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class EventTriggerModule(CollectionNodeModule):
+    """
+    class EventTriggerModule(CollectionNodeModule)
+
+        A module class for Event trigger node derived from CollectionNodeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the EventTriggerModule and it's base module.
+
+    * get_nodes(gid, sid, did)
+      - Method is used to generate the browser collection node.
+
+    * script_load()
+      - Load the module script for Event trigger, when any of the database node is
+        initialized.
+    """
+
+    NODE_TYPE = 'event_trigger'
+    COLLECTION_LABEL = gettext("Event Triggers")
+
+    def __init__(self, *args, **kwargs):
+        """
+        Method is used to initialize the EventTriggerModule and it's base module.
+
+        Args:
+            *args:
+            **kwargs:
+        """
+        super(EventTriggerModule, self).__init__(*args, **kwargs)
+        self.min_ver = 90300
+        self.max_ver = None
+
+    def get_nodes(self, gid, sid, did):
+        """
+        Generate the event_trigger node
+        """
+        yield self.generate_browser_collection_node(sid)
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for event_trigger, when any of the database node is
+        initialized.
+        """
+        return database.DatabaseModule.NODE_TYPE
+
+
+blueprint = EventTriggerModule(__name__)
+
+
+class EventTriggerView(PGChildNodeView):
+    """
+    class EventTriggerView(PGChildNodeView)
+
+        A view class for event trigger node derived from PGChildNodeView.
+        This class is responsible for all the stuff related to view like
+        updating event trigger node, showing properties, showing sql in sql
+        pane.
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the EventTriggerView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * list()
+      - This function is used to list all the event trigger nodes within
+        that collection.
+
+    * nodes()
+      - This function will used to create all the child node within that collection.
+        Here it will create all the event trigger node.
+
+    * properties(gid, sid, did, etid)
+      - This function will show the properties of the selected
+        event trigger node
+
+    * update(gid, sid, did, etid)
+      - This function will update the data for the selected event trigger node.
+
+    * msql(gid, sid, did, etid)
+      - This function is used to return modified SQL for the selected
+        event trigger node.
+
+    * get_sql(data, etid)
+      - This function will generate sql from model data
+
+    * sql(gid, sid, did, etid):
+      - This function will generate sql to show it in sql pane for the selected
+        event trigger node.
+
+    * get_event_funcs(gid, sid, did, etid):
+      - This function gets the event functions and returns an ajax response
+        for the event trigger node.
+
+    * dependents(gid, sid, did, etid):
+      - This function get the dependents and return ajax response for the
+        event trigger node.
+
+    * dependencies(self, gid, sid, did, etid):
+      - This function get the dependencies and return ajax response for the
+        event trigger node.
+    """
+
+    node_type = blueprint.node_type
+
+    parent_ids = [
+            {'type': 'int', 'id': 'gid'},
+            {'type': 'int', 'id': 'sid'},
+            {'type': 'int', 'id': 'did'}
+            ]
+    ids = [
+        {'type': 'int', 'id': 'etid'}
+        ]
+
+    operations = dict({
+        'obj': [
+            {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+            {'get': 'list', 'post': 'create'}
+        ],
+        'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+        'children': [{'get': 'children'}],
+        'sql': [{'get': 'sql'}],
+        'msql': [{'get': 'msql'}, {'get': 'msql'}],
+        'stats': [{'get': 'statistics'}],
+        'dependency': [{'get': 'dependencies'}],
+        'dependent': [{'get': 'dependents'}],
+        'module.js': [{}, {}, {'get': 'module_js'}],
+        'fopts': [{'get': 'get_event_funcs'}, {'get': 'get_event_funcs'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        return make_response(
+                render_template(
+                    "event_triggers/js/event_trigger.js",
+                    _=gettext
+                    ),
+                200, {'Content-Type': 'application/x-javascript'}
+                )
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+            self.conn = self.manager.connection(did=kwargs['did'])
+
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            ver = self.manager.version
+            if ver >= 90300:
+                self.template_path = 'event_triggers/sql/9.3_plus'
+
+            return f(*args, **kwargs)
+        return wrap
+
+
+    @check_precondition
+    def list(self, gid, sid, did):
+        """
+        This function is used to list all the event trigger
+        nodes within that collection.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+        sql = render_template("/".join([self.template_path, 'properties.sql']))
+        status, res = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+        return ajax_response(
+                response=res['rows'],
+                status=200
+                )
+
+    @check_precondition
+    def nodes(self, gid, sid, did):
+        """
+        This function is used to create all the child nodes within the collection.
+        Here it will create all the event trigger nodes.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+        result = []
+        sql = render_template("/".join([self.template_path, 'nodes.sql']))
+        status, res = self.conn.execute_2darray(sql)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        for row in res['rows']:
+            result.append(
+                    self.blueprint.generate_browser_node(
+                        row['oid'],
+                        did,
+                        row['name'],
+                        icon="icon-%s" % self.node_type
+                    ))
+
+        return make_json_response(
+                data=result,
+                status=200
+                )
+
+    @check_precondition
+    def properties(self, gid, sid, did, etid):
+        """
+        This function is used to list all the event trigger
+        nodes within that collection.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+        sql = render_template("/".join([self.template_path, 'properties.sql']), etid=etid, conn=self.conn)
+        status, res = self.conn.execute_dict(sql)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        result = res['rows'][0]
+        sec_labels = []
+
+        if 'seclabels' in result and result['seclabels'] is not None:
+            for sec in result['seclabels']:
+                sec = re.search(r'([^=]+)=(.*$)', sec)
+                sec_labels.append({
+                    'provider': sec.group(1),
+                    'securitylabel': sec.group(2)
+                    })
+        result.update({"seclabels": sec_labels})
+        return ajax_response(
+                response=result,
+                status=200
+                )
+
+    @check_precondition
+    def create(self, gid, sid, did):
+        """
+        This function will create a event trigger object.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+        required_args = {
+            'name':'Name',
+            'eventowner':'Owner',
+            'eventfunname':'Trigger function',
+            'enabled':'Enabled status',
+            'eventname':'Events'
+        }
+        err = []
+        for arg in required_args:
+            if arg not in data:
+                err.append(required_args.get(arg, arg))
+        if err:
+            return make_json_response(
+                    status=400,
+                    success=0,
+                    errormsg=gettext(
+                        "Couldn't find the required parameter/s %s." % err
+                    )
+                )
+        try:
+            sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+            sql = render_template("/".join([self.template_path, 'grant.sql']), data=data, conn=self.conn)
+            sql = sql.strip('\n').strip(' ')
+
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            sql = render_template("/".join([self.template_path, 'get_oid.sql']), data=data)
+            status, etid = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=etid)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    etid,
+                    did,
+                    data['name'],
+                    icon="icon-%s" % self.node_type
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, etid):
+        """
+        This function will update the data for the selected
+        event trigger node.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        try:
+            sql = self.get_sql(data, etid)
+            sql = sql.strip('\n').strip(' ')
+            if sql != "":
+                status, res = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']), data=data)
+                status, etid = self.conn.execute_scalar(sql)
+
+                return jsonify(
+                        node=self.blueprint.generate_browser_node(
+                            etid,
+                            did,
+                            data['name'],
+                            icon="icon-%s" % self.node_type
+                        )
+                    )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': etid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+    @check_precondition
+    def delete(self, gid, sid, did, etid):
+        """
+        This function will delete an existing event trigger object.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+        try:
+            sql = render_template("/".join([self.template_path, 'delete.sql']), etid=etid)
+            status, name = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=name)
+
+            sql = render_template("/".join([self.template_path, 'delete.sql']), name=name, cascade=cascade)
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=gettext("Event trigger dropped"),
+                data={
+                    'id': etid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, etid = None):
+        """
+        This function is used to return modified SQL for the selected
+        event trigger node.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+        data = {}
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except ValueError:
+                data[k] = v
+        try:
+            sql = self.get_sql(data, etid)
+            sql = sql.strip('\n').strip(' ')
+
+            return make_json_response(
+                    data=sql,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_sql(self, data, etid=None):
+        """
+        This function will generate sql from model data.
+
+        Args:
+          data: Contains the data of the selected event trigger node.
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+        required_args = [
+            'name'
+        ]
+
+        if etid is not None:
+            sql = render_template("/".join([self.template_path, 'properties.sql']), etid=etid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+            old_data = res['rows'][0]
+            for arg in required_args:
+                if arg not in data:
+                    data[arg] = old_data[arg]
+            sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data)
+        else:
+            required_args = {
+                'name':'Name',
+                'eventowner':'Owner',
+                'eventfunname':'Trigger function',
+                'enabled':'Enabled status',
+                'eventname':'Events'
+            }
+            err = []
+            for arg in required_args:
+                if arg not in data:
+                    err.append(required_args.get(arg, arg))
+            if err:
+                return make_json_response(
+                        status=400,
+                        success=0,
+                        errormsg=gettext(
+                            "Couldn't find the required parameter/s %s." % err
+                        )
+                    )
+            sql = render_template("/".join([self.template_path, 'create.sql']), data=data)
+            sql += "\n"
+            sql += render_template("/".join([self.template_path, 'grant.sql']), data=data)
+        return sql
+
+    @check_precondition
+    def sql(self, gid, sid, did, etid):
+        """
+        This function will generate sql to show in the sql pane for the selected
+        event trigger node.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+        try:
+            sql = render_template("/".join([self.template_path, 'properties.sql']), etid=etid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            result = res['rows'][0]
+
+            sql = render_template("/".join([self.template_path, 'create.sql']), data=result, conn=self.conn)
+            sql += "\n\n"
+            sql += render_template("/".join([self.template_path, 'grant.sql']), data=result, conn=self.conn)
+
+            db_sql = render_template("/".join([self.template_path, 'get_db.sql']), did=did)
+            status, db_name = self.conn.execute_scalar(db_sql)
+            if not status:
+                return internal_server_error(errormsg=db_name)
+
+            sql_header = "-- Event Trigger: {0} on database {1}\n\n-- ".format(result['name'], db_name)
+
+            sql_header += render_template(
+                    "/".join([self.template_path, 'delete.sql']),
+                    name=result['name'], )
+            sql_header += "\n"
+
+            sql = sql_header + sql
+
+            return ajax_response(response=sql)
+
+        except Exception as e:
+            return ajax_response(response=str(e))
+
+
+    @check_precondition
+    def get_event_funcs(self, gid, sid, did, etid=None):
+        """
+        This function gets the event functions and returns an ajax response
+        for the event trigger node.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          etid: Event trigger ID
+
+        Returns:
+
+        """
+        res = [{ 'label': '', 'value': '' }]
+        sql = render_template("/".join([self.template_path, 'eventfunctions.sql']))
+        status, rest = self.conn.execute_2darray(sql)
+        if not status:
+            return internal_server_error(errormsg=rest)
+        for row in rest['rows']:
+            res.append(
+                {'label': row['tfname'], 'value': row['tfname']}
+            )
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    @check_precondition
+    def dependents(self, gid, sid, did, etid=None):
+        """
+        This function gets the dependents and returns an ajax response
+        for the event trigger node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            etid: Event trigger ID
+        """
+        dependents_result = self.get_dependents(self.conn, etid)
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, etid):
+        """
+        This function gets the dependencies and returns an ajax response
+        for the event trigger node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            etid: Event trigger ID
+        """
+        dependencies_result = self.get_dependencies(self.conn, etid)
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+EventTriggerView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/img/coll-event_trigger.png b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/img/coll-event_trigger.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c339406fa325dad67f59141a6a08a5334debcda
GIT binary patch
literal 350
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?b1=6kiPU=rwT=MDBga5xP
z_8ouw|L>&#A2Xj{cR#w-VBI{+?aRZi97(#*xCE$_u_VYZn8D%MjWi&Kv%n*=7)X17
zvD?XPJ0K&^)5S4_<9f0{kWc~xb80dxhx5@;Pr)+*-bQ9j-mAoSvNTT3G+F96CDTtU
zys(goD^vGXfVVgEs_XYmg7@y()6wYo{DM|*UmqVUo8r>Dvy9XIn6#3t7H`cGJ>lb~
z`u6T@g>5h9G)5dcu;fSs3qw<vM2e~mdkWBI)e_f;l9a@fRIB8oR3OD*WMF8ZYiOiv
zU>IU#U}a)#Wn!vrU}|MxFz=n-DijU5`6-!cl@JXEmS7D))h1R3W)KahriZQpYGCkm
L^>bP0l+XkKAR%?3

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/img/event_trigger.png b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/img/event_trigger.png
new file mode 100644
index 0000000000000000000000000000000000000000..3b413e4a648c999c0dcc005aaaf02a710be4414b
GIT binary patch
literal 324
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPHF3h)VW1=6kiPU=rwT=MDBssBIz
z{{Mgc|Bu?=uTvji_Bye{WYZ$U_4Az$Y>2*ja-Gx8WkA)8B|(0{3=Yq3qyagc1s;*b
zK-vS0-A-oP0U3dwE{-7_*OL<(nB7!eIhs%2;5b!K7}#Lo<lL;#vt^6P2CZeEK7CSR
z6j;p6;jn5(+POKFoI0Dbu4-*CTXuK1IcI}p`Z<TQJF7XfW=>*a4r5?=RWIaqXXEsv
zK*Lo_Tq8<S5=&C8l8aJ-6oZk0p`osUp{|i}h=H*c5E&Y48<<%c7@VBUTY#b=H$Npa
rtrDccK-a($s3*k8*viDj%D_z9z!a$A)b!9bKn)C@u6{1-oD!M<5hib6

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/img/triggerbad.png b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/img/triggerbad.png
new file mode 100644
index 0000000000000000000000000000000000000000..6cc2fe96c8f234a73672cc73fd272a15fc7de4f9
GIT binary patch
literal 610
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf&j6ngS0LTG@1*|3#U-B}o%;Xd
z@BjbbfBg9M>-UfEKmPptck0#i>q$vR85k~vM;<wH;@IDBlcy{^>h66vGt;?!=F#Zb
zZ{_7j+`X5s+_2Bq>0@|!L|y;4va)YQMPKvs_E<Z73=4bj@BhxjGp@SlRA9*0+}tl2
z8K0AqK7@w8_XCQ1SoF-Bv2V}k#Ke!$(H}xW-uwE!b9XmezhLo!y|+?RKLi6seBZgb
zt<o{>KeX=}P-AfLdmo>7uC8zF>|a}0_%=`3<>K}sDCoVn_gfd&*XHK03=LmuYd7cA
zd<YDD=jHX**=44)*Gnz!7pkhy6%^h&IUjNJn(5;GTwY=QPpA1n&oh<;`2{mLJiCzw
z<Zu>vL>2>S4={E+nQaGTEbw%343W5;d-f{dp#&b*2lcZ1ns=Wr`2LXZ@Y}!gZH&=I
zLRA-+#NU!T_D{s!WsY3O^U7b0%|TU)i%;fkeASY}^oDQwY2NeVd+dG)a<mDAsWxTk
z^s!7@X<|1)aN^mgfg01U^cJ}~Yz@md*(!e}tNg;-uZo$P&5CZ1PnImpJtH2m?%Iu<
z@>M5p$oE-1tl3!?oFZs`=O(lN+cwV$Ps+oeiATx_obU^~(ExO-YKdz^NlIc#s#S7P
zDv)9@GB7mMH89pSum~|UvNANcGO*M(Ftai+xcqEI5{ic0{FKbJO57S2?H0HP)WG2B
L>gTe~DWM4fI7SDA

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/js/event_trigger.js b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/js/event_trigger.js
new file mode 100644
index 0000000..cba2097
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/js/event_trigger.js
@@ -0,0 +1,179 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+    // Extend the browser's node model class to create a security model
+    var SecurityLabelModel = pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          provider: undefined,
+          securitylabel: undefined
+        },
+        // Define the schema for the Security Label
+        schema: [
+          {id: 'provider', label:'Provider', type:'text', group: null, editable: true},
+          {id: 'securitylabel', label:'Security Label', type: 'text', group:null, extraHeaderClasses: 'cellwidth-40', editable: true},
+        ],
+        validate: function() {
+            // Clear any existing errors.
+
+            this.errorModel.clear()
+            if (_.isUndefined(this.get('provider')) || String(this.get('provider')).replace(/^\s+|\s+$/g, '') == '') {
+                var msg = '{{ _('Provider can not be empty!') }}';
+                this.errorModel.set('provider',msg);
+                return msg;
+            }
+            if (_.isUndefined(this.get('securitylabel')) || String(this.get('securitylabel')).replace(/^\s+|\s+$/g, '') == '') {
+                var msg = '{{ _('Security Label can not be empty!') }}';
+                this.errorModel.set('securitylabel',msg);
+                return msg;
+            }
+            return null;
+        }
+    });
+
+  // Extend the browser's collection class for event trigger collection
+  if (!pgBrowser.Nodes['coll-event_trigger']) {
+    var databases = pgAdmin.Browser.Nodes['coll-event_trigger'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'event_trigger',
+        label: '{{ _('Event Trigger') }}',
+        type: 'coll-event_trigger'
+      });
+  };
+
+  // Extend the browser's node class for event triggers node
+  if (!pgBrowser.Nodes['event_trigger']) {
+    pgAdmin.Browser.Nodes['event_trigger'] = pgAdmin.Browser.Node.extend({
+      parent_type: 'database',
+      type: 'event_trigger',
+      label: '{{ _('Event Trigger') }}',
+      hasSQL:  true,
+      hasDepends: true,
+      canDrop: true,
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_event_trigger_on_coll', node: 'coll-event_trigger', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Event Trigger...') }}',
+          icon: 'wcTabIcon pg-icon-event_trigger', data: {action: 'create'}
+        },{
+          name: 'create_event_trigger', node: 'event_trigger', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Event Trigger...') }}',
+          icon: 'wcTabIcon pg-icon-event_trigger', data: {action: 'create'}
+        }
+        ]);
+      },
+      // Define the model for event trigger node
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          oid: undefined,
+          name: undefined,
+          eventowner: undefined,
+          comment: undefined,
+          enabled: undefined,
+          eventfuncoid: undefined,
+          eventfunname: undefined,
+          eventname: undefined,
+          when: undefined,
+          xmin: undefined,
+          source: undefined,
+          language: undefined
+        },
+        // Define the schema for the event trigger node
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string',
+          type: 'text'
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'text', mode: ['properties']
+        },{
+          id: 'eventowner', label:'{{ _('Owner') }}', cell: 'string',
+          type: 'text', mode: ['edit','create'], node: 'role',
+          control: Backform.NodeListByNameControl
+        },{
+          id: 'comment', label:'{{ _('Comment') }}', type: 'multiline'
+        },{
+          id: 'enabled', label:'{{ _('Enabled status') }}',
+          type:"radio", group: "Definition", mode: ['edit','create'],
+          options: [
+            {label: "Enable", value: "O"},
+            {label: "Disable", value: "D"},
+            {label: "Replica", value: "R"},
+            {label: "Always", value: "A"}
+          ]
+        },{
+          id: 'eventfunname', label:'{{ _('Trigger function') }}',
+          type: 'text', control: 'node-ajax-options', group: "Definition",
+          url:'fopts'
+        },{
+          id: 'eventname', label:'{{ _('Events') }}',
+          type:"radio", group: "Definition", cell: 'string',
+          options: [
+            {label: "DDL COMMAND START", value: "DDL_COMMAND_START"},
+            {label: "DDL COMMAND END", value: "DDL_COMMAND_END"},
+            {label: "SQL DROP", value: "SQL_DROP"}
+          ]
+        },{
+          id: 'when', label:'{{ _('When') }}', type: 'multiline', group: "Definition",
+        },{
+          id: 'providers', label: 'Security Labels', type: 'collection', group: "Security Labels",
+          model: SecurityLabelModel, control: 'unique-col-collection', mode: ['edit', 'create'],
+          canAdd: true, canDelete: true, uniqueCol : ['provider'],
+          columns: ['provider','securitylabel']
+         }
+        ],
+        // event trigger model data validation.
+        validate: function() {
+          var msg = undefined;
+          // Clear any existing error msg.
+          this.errorModel.clear();
+
+          if (_.isUndefined(this.get('name'))
+              || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Event trigger name can not be empty!') }}';
+            this.errorModel.set('name', msg);
+            return msg;
+          }
+
+          if (_.isUndefined(this.get('eventowner'))
+              || String(this.get('eventowner')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Event trigger owner can not be empty!') }}';
+            this.errorModel.set('eventowner', msg);
+            return msg;
+          }
+
+          if (_.isUndefined(this.get('enabled'))) {
+            msg = '{{ _('Event trigger enabled status can not be empty!') }}';
+            this.errorModel.set('enabled', msg);
+            return msg;
+          }
+
+          if (_.isUndefined(this.get('eventfunname'))
+              || String(this.get('eventfunname')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = '{{ _('Event trigger function can not be empty!') }}';
+            this.errorModel.set('eventfunname', msg);
+            return msg;
+          }
+
+          if (_.isUndefined(this.get('eventname'))) {
+            msg = '{{ _('Event trigger event can not be empty!') }}';
+            this.errorModel.set('eventname', msg);
+            return msg;
+          }
+
+          return null;
+        }
+      })
+  });
+
+  }
+
+  return pgBrowser.Nodes['coll-event_trigger'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/create.sql
new file mode 100644
index 0000000..b195b58
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/create.sql
@@ -0,0 +1,7 @@
+{% if data %}
+CREATE EVENT TRIGGER {{ conn|qtIdent(data.name) }} ON {{data.eventname}}
+{% if data.when %}
+    WHEN TAG IN ({{data.when}})
+{% endif %}
+    EXECUTE PROCEDURE {{data.eventfunname}}();
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/delete.sql
new file mode 100644
index 0000000..828f776
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/delete.sql
@@ -0,0 +1,7 @@
+{% if etid %}
+SELECT e.evtname AS name FROM pg_event_trigger e
+WHERE e.oid={{etid}}::int;
+{% endif %}
+{% if name %}
+DROP EVENT TRIGGER IF EXISTS {{ conn|qtIdent(name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/eventfunctions.sql b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/eventfunctions.sql
new file mode 100644
index 0000000..a016586
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/eventfunctions.sql
@@ -0,0 +1,4 @@
+SELECT quote_ident(nspname) || '.' || quote_ident(proname) AS tfname
+FROM pg_proc p, pg_namespace n, pg_language l
+WHERE p.pronamespace = n.oid AND p.prolang = l.oid AND p.pronargs = 0 AND l.lanname != 'sql' AND prorettype::regtype::text = 'event_trigger'
+ORDER BY nspname ASC, proname ASC
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/get_db.sql b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/get_db.sql
new file mode 100644
index 0000000..dd8d251
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/get_db.sql
@@ -0,0 +1 @@
+SELECT db.datname as name FROM pg_database as db WHERE db.oid = {{did}}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/get_oid.sql
new file mode 100644
index 0000000..262ca93
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/get_oid.sql
@@ -0,0 +1,5 @@
+{# The Sql below will provide oid for newly created event_trigger #}
+{% if data %}
+SELECT e.oid from pg_event_trigger e
+WHERE e.evtname = {{ data.name|qtLiteral }}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/grant.sql
new file mode 100644
index 0000000..f8365ff
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/grant.sql
@@ -0,0 +1,26 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% if data %}
+{% if data.enabled and data.enabled != "O" %}
+ALTER EVENT TRIGGER {{ conn|qtIdent(data.name) }}
+{% if data.enabled == "D" %}
+    DISABLE;
+{% elif data.enabled == "R" %}
+    ENABLE REPLICA;
+{% elif data.enabled == "A" %}
+    ENABLE ALWAYS;
+{% endif %}
+{% endif %}
+
+{% if data.comment %}
+COMMENT ON EVENT TRIGGER {{ conn|qtIdent(data.name) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% if data.providers and data.providers|length > 0 %}
+
+{% for r in data.providers %}
+{{ SECLABLE.APPLY(conn, 'EVENT TRIGGER', data.name, r.provider, r.securitylabel) }}
+{% endfor %}{% endif %}
+
+ALTER EVENT TRIGGER {{ conn|qtIdent(data.name) }}
+    OWNER TO {{data.eventowner}};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/nodes.sql
new file mode 100644
index 0000000..dddd66f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/nodes.sql
@@ -0,0 +1,3 @@
+SELECT e.oid, e.evtname AS name
+    FROM pg_event_trigger e
+    ORDER BY e.evtname
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/properties.sql
new file mode 100644
index 0000000..0df12b3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/properties.sql
@@ -0,0 +1,17 @@
+SELECT e.oid, e.xmin, e.evtname AS name, upper(e.evtevent) AS eventname,
+pg_catalog.pg_get_userbyid(e.evtowner) AS eventowner,
+e.evtenabled AS enabled,
+e.evtfoid AS eventfuncoid, quote_ident(n.nspname) || '.' || e.evtfoid::regproc AS eventfunname,
+array_to_string(array(select quote_literal(x) from unnest(evttags) as t(x)), ', ') AS when,
+ pg_catalog.obj_description(e.oid, 'pg_event_trigger') AS comment,
+ (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=e.oid) AS seclabels,
+ p.prosrc AS source, p.pronamespace AS schemaoid, l.lanname AS language
+ FROM pg_event_trigger e
+ LEFT OUTER JOIN pg_proc p ON p.oid=e.evtfoid
+ LEFT OUTER JOIN pg_language l ON l.oid=p.prolang,
+ pg_namespace n
+ WHERE p.pronamespace = n.oid
+{% if etid %}
+ AND e.oid={{etid}}::int
+{% endif %}
+  ORDER BY e.evtname
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/update.sql
new file mode 100644
index 0000000..6f1bc70
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/templates/event_triggers/sql/9.3_plus/update.sql
@@ -0,0 +1,66 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% if data %}
+{% if (data.eventfunname and data.eventfunname != o_data.eventfunname) or
+  (data.eventname and data.eventname != o_data.eventname) or
+  (data.when and data.when != o_data.when) %}
+DROP EVENT TRIGGER IF EXISTS {{ conn|qtIdent(o_data.name) }};
+
+CREATE EVENT TRIGGER {{ conn|qtIdent(data.name) if data.name else conn|qtIdent(o_data.name) }} ON {{ data.eventname if data.eventname else o_data.eventname }}
+{% if data.when or o_data.when %}
+    WHEN TAG IN ({{ data.when if data.when else o_data.when }})
+{% endif %}
+    EXECUTE PROCEDURE {{ data.eventfunname if data.eventfunname else o_data.eventfunname }}();
+{% else %}
+
+{% if data.name and data.name != o_data.name %}
+ALTER EVENT TRIGGER {{ conn|qtIdent(o_data.name) }}
+    RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% endif %}
+
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON EVENT TRIGGER {{ conn|qtIdent(data.name) }}
+    IS '{{ data.comment }}';
+{% endif %}
+
+{% if data.enabled and data.enabled != o_data.enabled %}
+ALTER EVENT TRIGGER {{ conn|qtIdent(data.name) }}
+{% if data.enabled == "O" %}
+    ENABLE;
+{% elif data.enabled == "D" %}
+   DISABLE;
+{% elif data.enabled == "R" %}
+    ENABLE REPLICA;
+{% elif data.enabled == "A" %}
+    ENABLE ALWAYS;
+{% endif %}
+{% endif %}
+{% endif %}
+
+{% if data.providers and
+	data.providers|length > 0
+%}{% set seclabels = data.providers %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'EVENT TRIGGER', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'EVENT TRIGGER', data.name, r.provider, r.securitylabel) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'EVENT TRIGGER', data.name, r.provider, r.securitylabel) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+
+{% if data.eventowner and data.eventowner != o_data.eventowner %}
+ALTER EVENT TRIGGER {{ conn|qtIdent(data.name) }}
+    OWNER TO {{data.eventowner}};
+{% endif %}
\ No newline at end of file
-- 
Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers

Reply via email to