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